pocketlint-0.5.31/0000775000175000017500000000000011767132576014641 5ustar curtiscurtis00000000000000pocketlint-0.5.31/HACKING0000644000175000017500000000216711414654207015621 0ustar curtiscurtis00000000000000Contributing the the Gedit Developer Plugins project ==================================================== You can learn about the goals of the project at https://launchpad.net/pocket-lint The project uses Bazaar for version control and is hosted on Launchpad. You can get a copying of current code from the console using: bzr branch lp:pocket-lint Code style ---------- Python is expected to conform with PEP8, and running pocket-lint on itself should report no issues. Integrating pocket-lint to another tool --------------------------------------- The three minimal lines required to check the format of a source file are: from pocketlint.checkformat import Reporter, check_sources reporter = Reporter(Reporter.CONSOLE) check_sources(sources, reporter) `sources` is a list of file paths. `reporter` is the mechanism used to report issues. Reporter is a sink; the checkers do not know or care about what happens. You can implement your own Reporter by providing this signature: class MyReporter: def __call__(self, line_no, message, icon=None, base_dir=None, file_name=None): pocketlint-0.5.31/ChangeLog0000644000175000017500000012465211701061000016366 0ustar curtiscurtis00000000000000 2012-01-04 Curtis Hovey [431] Incremented version. 2012-01-04 Curtis Hovey [430] Updated help. 2012-01-04 Curtis Hovey [429] Added formatting options to formatcheck. 2012-01-04 Curtis Hovey [428] Remoced redundant code. 2012-01-04 Curtis Hovey [427] Extracted format_and_save. 2012-01-03 Curtis Hovey [426] Added line length tests for source and want cases. 2012-01-03 Curtis Hovey [425] Fixed indentation issue discovered in testing. 2012-01-03 Curtis Hovey [424] Added more fix header tests. 2012-01-03 Curtis Hovey [423] Revised indentation tests. 2012-01-03 Curtis Hovey [422] Added test for indentation corner case. 2012-01-03 Curtis Hovey [421] Added test to fix moin header. 2012-01-03 Curtis Hovey [420] Added test for fix bad intentation 2012-01-03 Curtis Hovey [419] Added test for fix trailing whitespace. 2012-01-03 Curtis Hovey [418] Added test for fix long lines. 2012-01-03 Curtis Hovey [417] Added long line test. 2012-01-03 Curtis Hovey [416] added trailing white-space test. 2012-01-03 Curtis Hovey [415] Added malformed and bad indentation doctest tests. 2012-01-03 Curtis Hovey [414] Added test for compilation error. 2012-01-03 Curtis Hovey [413] Added test for glob identifier. 2012-01-03 Curtis Hovey [412] Added a rudimentary test for doctests. 2012-01-03 Curtis Hovey [411] Update DocTestReviewer to use the new pyflakes. 2012-01-03 Curtis Hovey [410] Updated pep8 to 0.6.1. 2012-01-03 Curtis Hovey [409] Update PythonChecker and PyFlakes to work together. 2012-01-03 Curtis Hovey [408] Updated pyflakes to 0.5.0. 2012-01-03 Curtis Hovey [407] Merged Adi's fix for windows js. 2012-01-02 Curtis Hovey [406] Added test to remind me that pep8 checks 78 characters, bug Lp checks 78. 2011-12-20 Curtis Hovey [405] Lint twisted applications. 2011-11-15 Curtis Hovey [404] CSS statements can be empty. 2011-11-13 Curtis Hovey [403] Fixed lint. 2011-11-13 Curtis Hovey [402] Fix wrong Line length for lines with Unicode characters. 2011-11-13 Curtis Hovey [401] Add support for Python 2.5 2011-09-11 Curtis Hovey [400] Hush lint by using OR convention. 2011-09-11 Curtis Hovey [399] Define a common Seed object and import fulljslint. 2011-09-10 Curtis Hovey [398] Updated test to match changed in upstream lib. 2011-09-10 Curtis Hovey [397] Use any GI enabled JS interpreter: gjs or seed. 2011-06-06 Curtis Hovey [396] Use seed as the js interpreter. 2011-06-06 Curtis Hovey [395] Clear refernece. 2011-06-06 Curtis Hovey [394] Documented how to use jsreporter.js 2011-06-06 Curtis Hovey [393] Removed unused code. 2011-06-06 Curtis Hovey [392] Use seed as the default js command. 2011-06-06 Curtis Hovey [391] Updated attr name. 2011-06-06 Curtis Hovey [390] Removed js subprocess, but HTML5Browser is slow. 2011-06-05 Curtis Hovey [389] Updated documentation. 2011-06-05 Curtis Hovey [388] Minor speed changes. 2011-06-05 Curtis Hovey [387] Added rudimentary JS checker based on Webkit. 2011-05-10 Curtis Hovey [386] Incremented version. 2011-05-10 Curtis Hovey [385] merged Adi's fix. 2011-05-04 Curtis Hovey [384] Inremented version. 2011-05-04 Curtis Hovey [383] Corrected the js line number. 2011-05-04 Curtis Hovey [382] Catch and report IndentationExceptions. 2011-05-04 Curtis Hovey [381] Removed unneeded checks. 2011-05-04 Curtis Hovey [380] Remove trailing whitespace. 2011-05-04 Curtis Hovey [379] Merged python encoding fix. 2011-04-19 Curtis Hovey [378] Increment version. 2011-04-19 Curtis Hovey [377] Hush WindowError. 2011-04-19 Curtis Hovey [376] Move signed archives rules into setup.py. 2011-04-17 Curtis Hovey [375] Incremented version for cssccc 2011-04-17 Curtis Hovey [374] Merged cssccc lib. 2011-04-17 Curtis Hovey [373] Added a rule to sign the release after it is made. 2011-04-17 Curtis Hovey [372] Incremenet version for release. 2011-04-17 Curtis Hovey [371] Merged Adi's feature that returns the count od reported issues. 2011-04-17 Curtis Hovey [370] Added simple SQL checker. 2011-04-17 Curtis Hovey [369] Elaborated comment test. 2011-04-17 Curtis Hovey [368] Added a basic test to verify comments do not mess up blank line counts. 2011-04-17 Curtis Hovey [367] Update pep8 to version 0.6.1 2011-04-15 Curtis Hovey [366] Increment version. 2011-04-15 Curtis Hovey [365] Hacked pocketlint.contrib.pyflakes.checker to import from local version. 2011-04-14 Curtis Hovey [364] Remove the CSS loggin handler after parse so that multiple files are reported correctly. 2011-04-14 Curtis Hovey [363] Upgrades pyflakes to 0.4.0 2011-04-14 Curtis Hovey [362] Thou shalt not pass what thou hast not cast. 2011-04-11 Curtis Hovey [361] decrement version...nothing had changed. 2011-04-11 Curtis Hovey [360] rev version. 2011-04-09 Curtis Hovey [359] Increment version. 2011-04-09 Curtis Hovey [358] Added an pyflakes test for bad_syntax2_python. 2011-04-09 Curtis Hovey [357] Report TokenErrors when pep8 cannot parse the syntax. 2011-03-31 Curtis Hovey [356] ignore the build dir. 2011-03-31 Curtis Hovey [355] Explicitly state where to install the javascript files. 2011-03-29 Curtis Hovey [354] Increment version to suport Python 2.6. 2011-03-29 Curtis Hovey [353] Hush pyflakes. 2011-03-29 Curtis Hovey [352] Added suport for Python 2.6- to the test runner. Corrected XML lineno reporting. 2011-03-29 Curtis Hovey [351] Increment version. 2011-03-29 Curtis Hovey [350] Provide a dummy object for Python 2.6- if ParseError import fails. 2011-03-27 Curtis Hovey [349] Fixed date. 2011-03-27 Curtis Hovey [348] Incremented version for release. 2011-03-27 Curtis Hovey [347] Add js integration tests. 2011-03-27 Curtis Hovey [346] Do not test features the user does not have enabled. 2011-03-27 Curtis Hovey [345] CSSReporterHandler now recovers from exceptional message patters. 2011-03-27 Curtis Hovey [344] Added tests for ill formed markup. 2011-03-27 Curtis Hovey [343] Added a test verify entities are supported when there is no dtd. 2011-03-27 Curtis Hovey [342] Add xml text checker tests. Fixed a bug in the line_no reporting. 2011-03-27 Curtis Hovey [341] inline text that was not being reused. 2011-03-27 Curtis Hovey [340] Removed data module since it is not being reused. 2011-03-27 Curtis Hovey [339] Removed unused import. 2011-03-27 Curtis Hovey [338] Added text checking tests. 2011-03-27 Curtis Hovey [337] Added test target to makefile. 2011-03-27 Curtis Hovey [336] Extracted common test rules to CheckerTestCase. 2011-03-27 Curtis Hovey [335] Added python text tests. 2011-03-27 Curtis Hovey [334] Added tests for pep8. 2011-03-27 Curtis Hovey [333] Extracted common test code. 2011-03-27 Curtis Hovey [332] Added test for pyflakes warnings. 2011-03-27 Curtis Hovey [331] Added test for pyflakes IndentationError. 2011-03-27 Curtis Hovey [330] Updated check_pflakes handling of syntax errors. 2011-03-27 Curtis Hovey [329] Added a test simple test for python checking. 2011-03-27 Curtis Hovey [328] Added a simple test runner. 2011-03-26 Curtis Hovey [327] Added js file needed by the jslinter. 2011-03-26 Curtis Hovey [326] Updated fulljslint to the 2011-03-22 release. 2011-03-03 Curtis Hovey [325] Catch and report ParseError. 2010-10-01 Curtis Hovey [324] Incremented version for release. 2010-10-01 Curtis Hovey [323] Do not report singleton tuples as missing trailing space. 2010-08-04 Curtis Hovey [322] Increment version. 2010-08-04 Curtis Hovey [321] Fixed to the doctest linter. 2010-08-04 Curtis Hovey [320] Reports DocTestParser exceptions as warning. 2010-08-04 Curtis Hovey [319] Fixed bad lineno reports in doctests. 2010-08-04 Curtis Hovey [318] No not raise UndefinedName warning. Do not tamper with the doctest before it is parsed. 2010-08-03 Curtis Hovey [317] incremented version because of import change. 2010-08-03 Curtis Hovey [316] Hacked import of pyflakes messages to work from the context of pocketlint. 2010-08-01 Curtis Hovey [315] increment version because of an import change. 2010-08-01 Curtis Hovey [314] Hack pyflakes to know it is embdded. 2010-08-01 Curtis Hovey [313] Import from contrib. 2010-07-29 Curtis Hovey [312] Incremented the version. 2010-07-29 Curtis Hovey [311] Do not check log files because they are not source code. 2010-07-14 Curtis Hovey [310] Check the basepath and file_name when reporting the console. 2010-07-06 Curtis Hovey [309] Tell python 2.5 to stfu. 2010-07-06 Curtis Hovey [308] match .doctest as mime-type. 2010-07-06 Curtis Hovey [307] Removed unused format module. Incremented the version. 2010-07-06 Curtis Hovey [306] Report non-ascii chars in python that does not state it is utf-8. 2010-07-06 Curtis Hovey [305] Added checks for tabs. 2010-07-02 Curtis Hovey [304] Incremented to 0.3.0 2010-07-02 Curtis Hovey [303] Forced zpt and zcml into mime-types for ignorant systems. 2010-06-30 Curtis Hovey [302] Updated version. 2010-06-30 Curtis Hovey [301] Hushed lint. 2010-06-30 Curtis Hovey [300] Refactored jsreporter. 2010-06-29 Curtis Hovey [299] Added fulljslint to style checker. 2010-06-29 Curtis Hovey [298] Include standard text checks in python code. Check for pdb. 2010-06-29 Curtis Hovey [297] Updated console reporter to work explain what file the errors are in. 2010-06-29 Curtis Hovey [296] Added pocketlint script. 2010-06-29 Curtis Hovey [295] Correct the version number. 2010-06-29 Curtis Hovey [294] Include contrib in the dist package. 2010-06-29 Curtis Hovey [293] Added missing changelog. 2010-06-29 Curtis Hovey [292] Fixed the package and manifest rules. 2010-06-29 Curtis Hovey [291] Removed unused __init__.py 2010-06-29 Curtis Hovey [290] Added a makefile to keep this sane. 2010-06-28 Curtis Hovey [289] Added distutils/ 2010-06-28 Curtis Hovey [288] Do not use format.py. 2010-06-28 Curtis Hovey [287] Remove vestigial GDP code. 2010-06-21 Curtis Hovey [286] Added packages. 2010-06-21 Curtis Hovey [285] Small refactoring to get the formatter working again. 2010-06-21 Curtis Hovey [284] Removed unneeded plugins. 2010-06-21 Curtis Hovey [283] Brutal removal of non-lint files. This is now a fork. 2010-05-15 Curtis Hovey [282] Do not loose the selection during a replacement. 2010-05-15 Curtis Hovey [281] Replace the orginal text selection; do not loose it. 2010-04-27 Curtis Hovey [280] Inced the version to indicate the cssutils is now preferred. 2010-04-27 Curtis Hovey [279] Added a CSS syntax checker. 2010-04-26 Curtis Hovey [278] First draft of CSS formatter. 2010-04-17 Curtis Hovey [277] Incremented to 0.4.1. 2010-04-11 Curtis Hovey [276] Update the size rules of the find panel to reduce resizing when the actions are expanded. 2010-04-11 Curtis Hovey [275] do find when enter is pressed in the pattern entry. 2010-04-11 Curtis Hovey [274] Updated builder format 2010-04-09 Curtis Hovey [273] Implement CompletionProposal hash() and equal(). Improved the Info window to to use pydoc. 2010-04-06 Curtis Hovey [272] fxed typo in NEWS/ 2010-04-06 Curtis Hovey [271] When completing a closing tag, the closing angle-bracket is also inserted. 2010-04-06 Curtis Hovey [270] Refactored DynamicProvider.get_word to behave like english and programming langauges are. 2010-04-06 Curtis Hovey [269] Merged Lucid chages for gedit 2.30. 2009-12-05 Curtis Hovey [268] Updated news. Do not raise an exception because the closed and empty rules suck. 2009-11-29 Curtis Hovey [267] gdp 0.3.1. 2009-11-29 Curtis Hovey [266] Inc version to reflect new feature. 2009-11-29 Curtis Hovey [265] Support markup completion. 2009-11-28 Curtis Hovey [264] Fixed style. 2009-11-28 Curtis Hovey [263] Added test coverage for PythonChecker. 2009-11-28 Curtis Hovey [262] Fixed XMLChecker reporting of unknonwn entities. 2009-11-28 Curtis Hovey [261] Added test for formatcheckers. 2009-11-28 Curtis Hovey [260] Extracted AnyTextChecker rules to AnyTextMixin so that they can be reused by other classes. Fixed spelling. 2009-10-18 sinzui [259] Tuned the config. 2009-10-16 sinzui [258] Added a deactivate method to the PluginMixin to ensure that each plugin provides cleanup statements. 2009-10-16 sinzui [257] Removed unused macros. === 2.28.0, 0.2 === 2009-10-10 sinzui [256] Removed references to removed language-specs. 2009-10-10 sinzui [255] Updated the release notes. 2009-10-10 sinzui [254] Fixed url. 2009-10-10 sinzui [253] Removed unused imports. 2009-10-10 sinzui [252] Style fixes. 2009-10-10 sinzui [251] Removed unused files from build. 2009-10-10 sinzui [250] Removed import hack from syntaxcompleter. Let the testrunner do the work. 2009-10-09 sinzui [249] Added a hack and adjusted the tests to work in the distcheck environment. 2009-10-09 sinzui [248] autogen changes for upgrade to Karmic. 2009-10-09 sinzui [247] Do not translate fake gedit. 2009-10-09 sinzui [246] Updated the syntaxxompleter tests to verify the ui_manager changes. 2009-10-09 sinzui [245] Added a fake get_side_panel. 2009-10-08 sinzui [244] Improved the alignment of the line number column. 2009-10-08 sinzui [243] Added an dedicated cell renderer for line_no. Wrapping is much better, but the file path is separated from the icon because the line_no cell is always allocated width. 2009-10-07 sinzui [242] Store the file_pattern and search path. 2009-10-07 sinzui [241] Switched from lxml.etree to standard xml.etree. This implementation supports HTML entities, which is better than the XXX hack used for lxml. 2009-10-05 sinzui [240] Moved Check syntax and style into the side panel. Added support to check all open documents. 2009-10-05 sinzui [239] Place the find and replace plugin in the side panel. 2009-10-04 sinzui [238] Added support for gmissing. 2009-10-04 sinzui [237] Updated XXX comments with bug number, 2009-10-03 sinzui [236] Comprimsed between gdiff and writing the diff to file. 2009-10-03 sinzui [235] Added bazaar preferences. 2009-10-03 sinzui [234] Added support for send merge directives. 2009-10-03 sinzui [233] Move imports inline to avoid the seahorse dbus except when the session is cold. 2009-10-03 sinzui [232] Fixed titles. 2009-10-03 sinzui [231] Added gcheckout. 2009-10-03 sinzui [230] Added support for gbranch. 2009-10-03 sinzui [229] Added initialized_branch, 2009-10-03 sinzui [228] Removed comment. 2009-09-22 sinzui [227] Moved reformat doctest to the tools menu. 2009-09-22 sinzui [226] Added a save action to the file and replace widget. 2009-09-22 sinzui [225] Fixed active iter rule in find combobox. The liststore is sorted so the last index is rarely the location of the newly added text. Appending the text and calling the method again will do the right thing since the method activated exisitng text before trying to append text. 2009-09-21 sinzui [224] Refactored bzr-branch-open signal so that it is managed by the plugin. 2009-09-21 sinzui [223] Added python-syntax-error to signals. 2009-09-20 sinzui [222] Added gdpsyntaxcompleter to the menu. 2009-09-20 sinzui [221] Add an error dialog to explain to the user that the snippet plugin must be enabled before the gdpsyntaxcompleter 2009-09-20 sinzui [220] Fixed rule for identifing code comments that canses some comments in doctest to be wrongly reported as too long. 2009-09-20 sinzui [219] Added signal to add the path of the working tree to the finder widget when one is opened. Adjusted the level indentation of the file_lines results. 2009-09-19 sinzui [218] Set the working directory as the active item in the path_comboboxentry. 2009-09-19 sinzui [217] Removed the use of the 'activate' signal in the ui file because it overlaps with 'click'. 2009-09-15 sinzui [216] Revised the documentation. 2009-09-14 sinzui [215] Added autogen.sh to make hacking easier. Lowered the intltool requirement (incremented micro). 2009-09-14 sinzui [214] Do not make snippets. 2009-09-13 sinzui [213] Do not install snippets because they overwrite user data (bug 429072) === 0.1, 2.26.1 === 2009-09-13 sinzui [212] Added documentation. 2009-09-13 sinzui [211] Do not translate pyflakes because it is in contrib. 2009-09-13 sinzui [210] incremented the lib requirements for gtk.builder. 2009-09-13 sinzui [209] Gracefully recover from a Python SyntaxError when completing words. see bug 428713. 2009-09-12 sinzui [208] Added hack to XMLCheck to avoid false reports of entitiy problems. See lp bug 267825 and lp bug 410916 for the nature of the issue in lxml. 2009-09-12 sinzui [207] Fixed the jump_to behaviour when opening a file (bug 428451). Expand all the lines when there is only one file in the file-lines- view (bug 428441). 2009-09-09 sinzui [206] Updated docs. 2009-09-09 sinzui [205] Added Pyflakes to contrib to lower plugin dependencies. 2009-09-07 sinzui [204] fixes bug 425981. 2009-09-07 sinzui [203] Replaced the try/finally block with a callback to ensure the lock and un lock happens on the correct order 2009-09-07 sinzui [202] Included the tests in the dist package. 2009-09-07 sinzui [201] Fixed dist rules. The tarball is correct, but the tests should be included too. 2009-09-07 sinzui [200] Addedin missing HACKING. 2009-09-07 sinzui [199] Updated POTFILES.in with files that have translatable strings. 2009-09-07 sinzui [198] Added copyright to plugin libs. 2009-09-07 sinzui [197] Added copyright to plugins. 2009-09-07 sinzui [196] Added copyright to utils. 2009-09-07 sinzui [195] Removed the external tools dir. The scripts are too old to be used. The launchpad project plugin will provide the wanted functions. 2009-09-07 sinzui [194] Added copyright and linted. 2009-09-07 sinzui [193] Added copyright to adapted lang files. 2009-09-07 sinzui [192] Added copyright notifices to files. 2009-09-07 sinzui [191] Added a setup and teardown to the test suite to manages gedit's addition of gettext to builtins. 2009-09-07 sinzui [190] Updated syntax completer to use the other plugin infrastructure.. 2009-09-07 sinzui [189] Removed lint. 2009-09-07 sinzui [188] The intent of the test is no loger valid. Changed it to do a rudimentary test that the Finder can be created. 2009-09-07 sinzui [187] Updated def to provide a panel for tests. 2009-09-07 sinzui [186] Removed the activate method from the mixin. 2009-09-07 sinzui [185] Removed gedit dependency in the mixin because None can be used for the default document encoding. 2009-09-07 sinzui [184] Converted gdp*.actions to a method to decouple the controller methods. 2009-09-06 sinzui [183] Revised the position of menus. 2009-09-06 sinzui [182] Removed unused snippet and made placeholder menus consistent. 2009-09-05 sinzui [181] Added missing action to search menu. 2009-09-04 sinzui [180] Fixed some snippets. 2009-09-04 sinzui [179] Removed lint from the syntaxcompleter 2009-09-04 sinzui [178] introduced GDPWindow to decouple the plugin from the GeditWindow. 2009-09-03 sinzui [177] Added tabs to spaces. 2009-09-03 sinzui [176] Added find in 2009-09-02 sinzui [175] Fix the bug lp:423342. Also adjust the find widget. 2009-09-02 sinzui [174] Added bzr-gtk merge. 2009-09-02 sinzui [173] Added bzr-gtk visualise. 2009-09-01 sinzui [172] Added bzr-gtk tags 2009-09-01 sinzui [171] Added bzr-gtk ginfo and gconflicts. 2009-09-01 sinzui [170] Added icons to messages. 2009-09-01 sinzui [169] Switched from glade to gtkbuilder. 2009-09-01 sinzui [168] Switch from glade to gtkbuilder. There is one problematic glade file left. 2009-09-01 sinzui [167] Fixed exception when opening a file outside of the working tree. 2009-09-01 sinzui [166] Refacted the checks into smaller pieces. Added a simple XML checker to verify well-formedness. 2009-09-01 sinzui [165] Added pep8 style checking. 2009-09-01 sinzui [164] Added pep8.py to contrib 2009-08-31 sinzui [163] Added doctest checking. 2009-08-31 sinzui [162] Refacted the Reporter and BaseChecker to simplify extensions. 2009-08-31 sinzui [161] Show the Check syntax and styles panel when a file is checked. 2009-08-31 sinzui [160] Added rudimentary text and python syntax and style checkers. The initialisation and message reporting is bad. 2009-08-30 sinzui [159] Renamed match_view to file_lines_view 2009-08-30 sinzui [158] Add support to open and file and goto the line number from the file-lines-view. 2009-08-30 sinzui [157] Extracted the rules to setup the file-lines-view. 2009-08-30 sinzui [156] Added Replace in files to the menu. 2009-08-30 sinzui [155] Exposed replace in files. 2009-08-30 sinzui [154] updated formatting 2009-08-30 sinzui [153] Exposed the file pattern and directory path for searching. 2009-08-28 sinzui [152] Icons work some of the time. 2009-08-28 sinzui [151] Saving file icon even though it does not work. 2009-08-28 sinzui [150] Added support for mime-type in the model. 2009-08-28 sinzui [149] Use two cells to separate the lineno from the match/filename 2009-08-28 sinzui [148] Do not mutate the view after the model is set. 2009-08-28 sinzui [147] Updated UI 2009-08-28 sinzui [146] Show the matches in a treeview. The lineno needs its own column and there is a error that does not seemt to break the app: find.py:110: GtkWarning: A floating object was finalized. This means that someone called g_object_unref() on an object that had only a floating reference; the initial floating reference is not owned by anyone and must be removed with g_object_ref_sink(). match_view.append_column(self.column) 2009-08-28 sinzui [145] Fixed some menu nonsense that was appearing on the console. 2009-08-28 sinzui [144] Extracted the update_match_text rules. 2009-08-28 sinzui [143] The combobox's liststore is sorted and unique. 2009-08-28 sinzui [142] Add simple log to gdpfind as shim to build the UI. This provides the same report that comes from the command line. 2009-08-27 sinzui [141] Added simple support for case insensitivity and re escaping. 2009-08-27 sinzui [140] Added a non-working find panel. 2009-08-27 sinzui [139] Fixed syntax highlighting of doctests. 2009-08-27 sinzui [138] Removed unused tools. 2009-08-27 sinzui [137] Close the push dialog when it is finished. 2009-08-27 sinzui [136] Added gpush to gdpbzr. 2009-08-27 sinzui [135] Added gannotate to gdpbzr. 2009-08-27 sinzui [134] Removed unused tools. 2009-08-27 sinzui [133] Added status to gdpbzr. 2009-08-27 sinzui [132] Close the commit dialog when the user is done. 2009-08-27 sinzui [131] Used gdpbzr to commit itself. 2009-08-27 sinzui [130] Added diff of changes to push. 2009-08-27 sinzui [129] Added diff from parent. 2009-08-27 sinzui [128] Revered the use of jump_to because it creates unwanted tabs. 2009-08-27 sinzui [127] Use jump_to instead of manually calling set active on the tab. 2009-08-27 sinzui [126] Added diff uncommitted changes. Removed redundant tool. 2009-08-27 sinzui [125] Moved methods to make the class easier to read. 2009-08-27 sinzui [124] Refactored the push_tree and parent_tree to be DRY. 2009-08-27 sinzui [123] Added open divierged files from parent. Removed redundant tools. 2009-08-27 sinzui [122] Added open changed files that have not been pushed. 2009-08-27 sinzui [121] Rearranged method for readability. 2009-08-27 sinzui [120] Renamed method to describe what it really does. 2009-08-26 sinzui [119] Fixed grammar. 2009-08-26 sinzui [118] Toggle the sensitivity of bzr project menu items based on the need for a working tree. 2009-08-26 sinzui [117] Added a rule to use the active document or cwd to get th working tree via open_containing() 2009-08-26 sinzui [116] Added open_changed_files to gdpbzr. 2009-08-26 sinzui [115] Install po files in .local for home installs. 2009-08-26 sinzui [114] Removed hack to make the build work. 2009-08-26 sinzui [113] Fixed bad file names that were breaking the builld. 2009-08-26 sinzui [112] Add a scketch of the gdpbzr plugin. 2009-08-26 sinzui [111] Hack to make the plugin-files build 2009-08-26 sinzui [110] Hack to make the plugin-files build 2009-08-26 sinzui [109] Made plugin names consistent. 2009-08-26 sinzui [108] Moved formatdoctest into the format plugin. Removed redundant tools. 2009-08-25 sinzui [107] Fixed method name. 2009-08-25 sinzui [106] Refactored the wrapping rules. 2009-08-25 sinzui [105] Removed redundant tools. 2009-08-25 sinzui [104] All the selection commands now run in the format menu. 2009-08-25 sinzui [103] Added Formatter to work with plugin. The sort import and line ending methods work. 2009-08-25 sinzui [102] Added first draft of the format menu. 2009-08-24 sinzui [101] Updated tools. 2009-08-24 sinzui [100] Updated snippets. 2009-08-24 sinzui [99] Updated configure.ac to work with jaunty. 2009-03-05 sinzui [98] Added missing files. 2009-03-05 sinzui [97] Added tools, snippets, and langs from my own setup. 2009-03-05 sinzui [96] Added new test. 2009-03-05 sinzui [95] Refactored the find code. 2008-08-25 Curtis Hovey [94] Roughed out the FindPlugin test. 2008-08-25 Curtis Hovey [93] Added a test for FindPlugin.activate(). Added a fake Window.get_ui_manager to test the method. 2008-08-25 Curtis Hovey [92] Added a basic FindPlugin test. 2008-08-25 Curtis Hovey [91] Added a rough version of the FindGenerator and FindController based on the find.py shell script. Tests are needed to define the correct API. 2008-08-25 Curtis Hovey [90] Fixed typo cause by renaming. 2008-08-25 Curtis Hovey [89] Renamed searchplugin to findplugin. Add glade file to makefile.am 2008-08-25 Curtis Hovey [88] Added a glade file to provide UI elements. 2008-08-25 Curtis Hovey [87] Added basic plugin for search multiple files. There is enough in place to write tests and add the core feature. 2008-08-25 Curtis Hovey [86] Fixed line indexing issue caused by end_iter can be after the last index of of the buffer (WTF). 2008-08-24 Curtis Hovey [85] Added test coverage for PythonGenerator._get_parsable_text. Added support for keywords as candidates for symbol completion. 2008-08-24 Curtis Hovey [84] Revised the rules for 'authoritative' added rules to make the source code compilable when the current line is invalid. 2008-06-28 Curtis Hovey [83] Extracted get_string_before_cursor and ensure_prefix. The tests need revision. get_words now returns a 2-tuple of (is_authoratative, words). 2008-06-24 Curtis Hovey [82] Extracted get_word from get_word_prefix to allow the PythonSyntaxGenerator to see the python identifier. 2008-06-23 Curtis Hovey [81] Renamed methods to follow PEP 8. 2008-06-23 Curtis Hovey [80] Added a test to show that create_list is a unique list of words. Renamed SyntaxCompleter methods to follow PEP 8. Updated docstrings. 2008-06-23 Curtis Hovey [79] Minor improvements to the SyntaxModel. The PythonSyntaxGenerator appears to work, but there is stability issues. More testing is need to fix this. 2008-06-22 Curtis Hovey [78] Added make rules for ChangeLog. Decided to sync plugin versions with gedit to clearly state the intended compatability. 2008-06-22 Curtis Hovey [77] autoconf; configure; make; make install "Just Works". 2008-06-08 Curtis Hovey [76] Documented a place-holder fix can be removed.. 2008-06-08 Curtis Hovey [75] Minor updates because of system upgrade. 2007-12-28 Curtis Hovey [74] Underscores are non-breaking spaces--they are considered a word character when creating the list of words in a document. 2007-10-30 Curtis Hovey [73] Added a destroy method to restor the SnippetCompleter.model so that Snippets continue to work after syntax is used. Add a kludge to handle a situation where createList is being passed a list instead of a document. 2007-10-29 Curtis Hovey [72] Updated ignored. 2007-10-29 Curtis Hovey [71] Unversioned aclocal.m4. 2007-10-29 Curtis Hovey [70] Added i18n. 2007-10-29 Curtis Hovey [69] Added base effort to use autotools for l10n and installation. 2007-10-28 Curtis Hovey [68] Replaced signal test with direct call to destroy to avoid gtk- warnings. 2007-10-28 Curtis Hovey [67] Restructured testrunner to divide tests into suites by directory, and run a single report. 2007-10-28 Curtis Hovey [66] Restructured testrunner to divide tests into suites by directory 2007-10-27 Curtis Hovey [65] Fixed tests to match new object reprs. 2007-10-07 Curtis Hovey [64] Added gettext to builtins following gedit's example. 2007-10-07 Curtis Hovey [63] Updated plugin to use gtksourceview2. There is a gtkTextView assertion error that needs fixing. 2007-09-01 Curtis Hovey [62] Evil touchpad. 2007-09-01 Curtis Hovey [61] Revised the _calculateSyntaxViewPosition() test to use a dummy get_origin to ensure the window is correctly positioned. Used proof for the last check because screen width's vary. 2007-08-31 Curtis Hovey [60] Replaced the source tuple with just the gedit.Document because it has all the infomation added to sources. 2007-08-31 Curtis Hovey [59] Revised the Fake to use a scrolledwindow to ensure the window size is retrained in testing. 2007-08-31 Curtis Hovey [58] Cleaned up the Fake behaviour. 2007-08-31 Curtis Hovey [57] Added Dummy to the Fake implementations. 2007-08-31 Curtis Hovey [56] Completed plugin doctests. 2007-08-30 Curtis Hovey [55] Completed fake implementation off the core gedit objects. The Dummy is not used at the moment, but it will be kept since it is very good at providing adhoc data. 2007-08-30 Curtis Hovey [54] Moved the testing doctests to tests to ensure that they are run. Added a fake Document class for loading files and checking metadata. 2007-08-29 Curtis Hovey [53] Pushed the most of the dummy setup into gedithelpers.get_window. Add some plugin tests. 2007-08-29 Curtis Hovey [52] Updated overrides to support get_window_group. Updated fakegen to support functions. 2007-08-29 Curtis Hovey [51] Added a Dummy variable to all Fake modules for easy access by functions and methods. Added a fake implementation of get_app_default since it must always return an instance of Gedit.App. 2007-08-29 Curtis Hovey [50] Pushed universal testing objects and functions to testing, moved remaining function to gedithelpers. Added the universal objected and function to the test suite's globs. 2007-08-28 Curtis Hovey [49] Added rules to define fake functions. Revised the rules for Dummy data. 2007-08-28 Curtis Hovey [48] Renamed Fake to Dummy, and moved it to testing. Updated Dummy to behave like a singleton dictionary. Updated tests to use Dummy. Add gen-gedit to regenerate gedit fake py files. 2007-08-28 Curtis Hovey [47] Added overrides handling. Added DefsOverridesMixer to manage the writing of both source of data. 2007-08-28 Curtis Hovey [46] Renamed mock.* to fake.*. In the long term, the fake class generator will need both stub and mock features, but they will be provided by testing so that code need not be regenerated when testing features are added. 2007-08-26 Curtis Hovey [45] Moved mockgen to testing 2007-08-26 Curtis Hovey [44] syntaxcompleterplugin started. Mock needs a thorough revision before the tests can be completed. 2007-08-19 Curtis Hovey [43] Removed test hook hack. Tests for the controller are complete. 2007-08-19 Curtis Hovey [42] Tests for SyntaxController are complete. There is a testing hook in showSyntaxView that should be removed. 2007-08-19 Curtis Hovey [41] Added more signal testing helpers and one signal test. 2007-08-19 Curtis Hovey [40] Added tests. Removed SyntaxController.updateLanguage(). 2007-08-18 Curtis Hovey [39] Added missing doctest. Added additional tests for the SyntaxController. Refactored a few methods. 2007-08-18 Curtis Hovey [38] Refactored the SyntaxController. Some additional test coverage is needed. 2007-08-16 Curtis Hovey [37] Change SyntaxControler to descend from object to remove the gedit and snippet dependancies. 2007-08-13 Curtis Hovey [36] Reworded mock testing. Not a very good session. However, if/when gtk.main() is run after controller.show_completion(), the feature will really run in an interactive session. 2007-08-12 Curtis Hovey [35] Moved heleprs to new modules testing. Added base test for SyntaxController. Method testing is needed. 2007-08-12 Curtis Hovey [34] Moved testing helpers to new testing module. 2007-08-01 Curtis Hovey [33] Introduced SignalTester and proof to make testing easier. 2007-07-31 Curtis Hovey [32] Added a hack and a base test to create a SyntaxView with the SyntaxModel. 2007-07-31 Curtis Hovey [31] Added a Mock singleton to pass data between the test and the mock classes. 2007-07-31 Curtis Hovey [30] Removing other plugins. 2007-07-31 Curtis Hovey [29] Removing other plugins. 2007-07-31 Curtis Hovey [28] removing other plugings 2007-07-30 Curtis Hovey [27] Fixed the use of sources. 2007-07-29 Curtis Hovey [26] Added model tests. Replaced the model with a sensible implementation. 2007-07-28 Curtis Hovey [25] Renamed CompleteModel to SyntaxModel. Need to work out how to make sure SyntaxModel is used instead of CompleteModel. 2007-07-28 Curtis Hovey [24] Refactored syntax completer. 2007-07-26 Curtis Hovey [23] Pulled the plugin code together. This is very bad. The bottom is tested and understood. The top is neither. The middle needs tests. Everything needs another round of naming. 2007-07-25 Curtis Hovey [22] Moved tests into gdp. Removed the forced addition of the prefix to the SyntaxModel because that is something else's responsability. 2007-07-22 Curtis Hovey [21] Extended tests. Clean up SyntaxControler. I need to understand it better to write tests and create logic that is needed for syntax completion. 2007-07-21 Curtis Hovey [20] Hooked TextModel into CompleteModel. 2007-07-21 Curtis Hovey [19] Added the missing test pieces. 2007-07-21 Curtis Hovey [18] Added some basic tests. 2007-07-21 Curtis Hovey [17] Renamed *Context => *Model to make its purpose clear. 2007-07-19 Curtis Hovey [16] Did preliminary work on CompleteModel. Testing is needed. 2007-07-18 Curtis Hovey [15] Added tests for TextContext. Fixed a bug that stopped hyphenated words from matching. 2007-07-09 Curtis Hovey [14] Added missing files from last commit. 2007-07-09 Curtis Hovey [13] Rearranged test infrastructure so that the config file is executible, and the testrunner was moved to utils. 2007-07-09 Curtis Hovey [12] Started Context classes, revised test infrastructure. 2007-07-08 Curtis Hovey [11] Compatability changes to with with zope testing. 2007-07-08 Curtis Hovey [10] Revised commandline arguments and rc. Improved colourization of test output. 2007-07-08 Curtis Hovey [9] Added bold and colors to test output. 2007-07-08 Curtis Hovey [8] Revised test setup. 2007-07-02 Curtis Hovey [7] restructured project. Added Mock object generator in utils/ to make gedit/. 2007-07-01 Curtis Hovey [6] Partial changes for new syntax checker. New work coexists with the old work 2007-06-17 Curtis Hovey [5] Refactored the logic for readability. 2007-06-16 Curtis Hovey [4] Added language knowledge to the completer to allow it to knwo when to complete Python symbols. 2007-06-16 Curtis Hovey [3] Cleaned up pylint errors about gedit_pylint. Modified it to work with any PyLint that supports --output-format. 2007-06-16 Curtis Hovey [2] Renamed directory. 2007-06-16 Curtis Hovey [1] Added first plugins. pocketlint-0.5.31/AUTHORS0000644000175000017500000000006611412215150015662 0ustar curtiscurtis00000000000000Active authors: Curtis Hovey pocketlint-0.5.31/README0000644000175000017500000000254611414654157015517 0ustar curtiscurtis00000000000000General Information =================== Pocket-lint a composite linter and style checker. It has several notable features: * Provides a consistent report of issues raised by the subordinate checkers. * Alternate Reports can be written to change the report, or integrate the report into another application. * Supports checking of multiple source types: * Python syntax and style * Python doctest style * XML/HTML style and entities * CSS style * JavaScript syntax and style * Plain text * Supports reporting: * Python doctests * CSS * XML/HTML Installation from deb --------------------- Debian packages are available at: https://launchpad.net/~sinzui/+archive/ppa Adding this archive to your sources will get you the latest updated as they are released. Installation from tarball ------------------------- run make install For more control over your options see python ./setup.py --help Reporting bugs and contributing ------------------------------- You can learn details about the pocket-lint project, ask questions, and report bugs at https://launchpad.net/pocket-lint Contrib ------- pocket-lint includes the work of other projects when it simplifies dependencies. * pep8.py (Expat license) * pyflakes (MIT license) * fulljslint (MIT-style license) pocketlint-0.5.31/PKG-INFO0000664000175000017500000000042311767132576015735 0ustar curtiscurtis00000000000000Metadata-Version: 1.0 Name: pocketlint Version: 0.5.31 Summary: Pocket-lint a composite linter and style checker. Home-page: https://launchpad.net/pocket-lint Author: Curtis C. Hovey Author-email: sinzui.is@verizon.net License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN pocketlint-0.5.31/COPYING0000644000175000017500000000206511412215150015646 0ustar curtiscurtis00000000000000Copyright (c) 2010 Curtis C. Hovey The MIT License: 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. pocketlint-0.5.31/scripts/0000775000175000017500000000000011767132576016330 5ustar curtiscurtis00000000000000pocketlint-0.5.31/scripts/pocketlint0000755000175000017500000000014011412442535020405 0ustar curtiscurtis00000000000000#!/usr/bin/python import sys from pocketlint.formatcheck import main sys.exit(main(sys.argv)) pocketlint-0.5.31/INSTALL0000644000175000017500000000003511412215150015637 0ustar curtiscurtis00000000000000# IS this Makefile or setup? pocketlint-0.5.31/pocketlint/0000775000175000017500000000000011767132576017015 5ustar curtiscurtis00000000000000pocketlint-0.5.31/pocketlint/formatdoctest.py0000755000175000017500000003670011715014034022231 0ustar curtiscurtis00000000000000#!/usr/bin/python # Copyright (C) 2009-2010 - Curtis Hovey # This software is licensed under the MIT license (see the file COPYING). """Reformat a doctest to Launchpad style.""" from __future__ import with_statement __metaclass__ = type __all__ = [ 'DoctestReviewer', ] import _ast import os import re import sys from difflib import unified_diff from doctest import DocTestParser, Example from optparse import OptionParser from textwrap import wrap from contrib.pyflakes.checker import Checker class DoctestReviewer: """Check and reformat doctests.""" rule_pattern = re.compile(r'([=~-])+[ ]*$') moin_pattern = re.compile(r'^(=+)[ ](.+)[ ](=+[ ]*)$') SOURCE = 'source' WANT = 'want' NARRATIVE = 'narrative' def __init__(self, file_path, doctest, reporter=None, options=None): self.doctest = doctest self.file_path = file_path self.base_dir = os.path.dirname(file_path) self.file_name = os.path.basename(file_path) self.blocks = [] self.block = [] self.block_method = self.preserve_block self.code_lines = [] self.example = None self.last_bad_indent = 0 self.has_printed_filename = False self._reporter = reporter self.options = options def get_parts(self): parser = DocTestParser() try: return parser.parse(self.doctest, self.file_path) except ValueError, error: self._print_message(str(error), 0) return [] def _print_message(self, message, lineno): """Print the error message with the lineno. :param message: The message to print. :param lineno: The line number the message pertains to. """ if self._reporter is None: if not self.has_printed_filename: print '%s:' % self.file_path self.has_printed_filename = True print ' % 4s: %s' % (lineno, message) else: self._reporter( int(lineno), message, base_dir=self.base_dir, file_name=self.file_name) def _is_formatted(self, text): """Return True if the text is pre-formatted, otherwise False. :param: text a string, or a list of strings. """ if isinstance(text, list): text = text[0] return text.startswith(' ') def _walk(self, doctest_parts): """Walk the doctest parts; yield the line and kind. Yield the content of the line, and its kind (SOURCE, WANT, NARRATIVE). SOURCE and WANT lines are stripped of indentation, SOURCE is also stripped of the interpreter symbols. :param doctest_parts: The output of DocTestParser.parse. """ for part in doctest_parts: if part == '': continue if isinstance(part, Example): self.example = part for line in part.source.splitlines(): kind = DoctestReviewer.SOURCE yield line, kind for line in part.want.splitlines(): kind = DoctestReviewer.WANT yield line, kind else: self.example = None kind = DoctestReviewer.NARRATIVE for line in part.splitlines(): yield line, kind def _apply(self, line_methods): """Call each line_method for each line in the doctest. :param line_methods: a list of methods that accept lineno, line, and kind as arguments. Each method must return the line for the next method to process. """ self.blocks = [] self.block = [] lineno = 0 previous_kind = DoctestReviewer.NARRATIVE for line, kind in self._walk(self.get_parts()): lineno += 1 # Some method could check if the line number is one that # is skipped by doctest parser and increment the number again. # line probably needs to get a \n add to it too. self._append_source(kind, line) if kind != previous_kind and kind != DoctestReviewer.WANT: # The WANT block must adjoin the preceding SOURCE block. self._store_block(previous_kind) for method in line_methods: line = method(lineno, line, kind, previous_kind) if line is None: break if not line: continue self.block.append(line) previous_kind = kind # Capture the last block and a blank line. self.block.append('\n') self._store_block(previous_kind) def _append_source(self, kind, line): """Update the list of source code lines seen.""" if kind == self.SOURCE: self.code_lines.append(line) else: self.code_lines.append('') def _store_block(self, kind): """Append the block to blocks, re-wrap unformatted narrative. :param kind: The block's kind (SOURCE, WANT, NARRATIVE) """ if len(self.block) == 0: return block = self.block_method(kind, self.block, self.blocks) self.blocks.append('\n'.join(block)) self.block = [] def check(self): """Check the doctest for style and code issues. 1. Check line lengths. 2. Check that headings are not in Moin format. 3. Check indentation. 4. Check trailing whitespace. """ self.code_lines = [] self.last_bad_indent = 0 self.block_method = self.preserve_block self.example = None self.has_printed_filename = False self.check_source_comments() line_checkers = [ self.check_length, self.check_heading, self.check_indentation, self.check_trailing_whitespace, ] self._apply(line_checkers) code = '\n'.join(self.code_lines) self.check_source_code(code) def format(self): """Reformat doctest. 1. Tests are reindented to 4 spaces. 2. Simple narrative is rewrapped to 78 character width. 3. Formatted (indented) narrative is preserved. 4. Moin headings are converted to RSR =, == , and === levels. 5. There is one blank line between blocks, 6. Except for headers which have two leading blank lines. 7. All trailing whitespace is removed. SOURCE and WANT long lines are not fixed--this is a human operation. """ self.code_lines = [] line_checkers = [ self.fix_trailing_whitespace, self.fix_indentation, self.fix_heading, self.fix_narrative_paragraph, ] self.block_method = self.format_block self._apply(line_checkers) self.block_method = self.preserve_block return '\n\n'.join(self.blocks) def preserve_block(self, kind, block, blocks): """Do nothing to the block. :param kind: The block's kind (SOURCE, WANT, NARRATIVE) :param block: The list of lines that should remain together. :param blocks: The list of all collected blocks. """ return block def format_block(self, kind, block, blocks): """Format paragraph blocks. :param kind: The block's kind (SOURCE, WANT, NARRATIVE) :param block: The list of lines that should remain together. :param blocks: The list of all collected blocks. """ if kind != DoctestReviewer.NARRATIVE or self._is_formatted(block): return block try: rules = ('===', '---', '...') last_line = block[-1] is_heading = last_line[0:3] in rules and last_line[-3:] in rules except IndexError: is_heading = False if len(blocks) != 0 and is_heading: # Headings should have an extra leading blank line. block.insert(0, '') elif is_heading: # Do nothing. This is the first heading in the file. pass else: long_line = ' '.join(block).strip() block = wrap(long_line, 72) return block def check_source_comments(self): """Comments are not appropiate in source examples.""" for lineno, line in enumerate(self.doctest.splitlines()): if '>>> #' in line or '... #' in line: self._print_message( 'Comment belongs in narrative.', lineno + 1) def is_code_comment(self, line): """Return True if the line is a code comment.""" comment_pattern = re.compile(r'^\s+#') return comment_pattern.match(line) is not None def check_length(self, lineno, line, kind, previous_kind): """Check the length of the line. Each kind of line has a maximum length: * NARRATIVE: 78 characters. * SOURCE: 70 characters (discounting indentation and interpreter). * WANT: 74 characters (discounting indentation). """ if kind == DoctestReviewer.NARRATIVE and self.is_code_comment(line): # comments follow WANT rules because they are in code. kind = DoctestReviewer.WANT line = line.lstrip() length = len(line) if kind == DoctestReviewer.NARRATIVE and length > 78: self._print_message('%s exceeds 78 characters.' % kind, lineno) elif kind == DoctestReviewer.WANT and length > 74: self._print_message('%s exceeds 78 characters.' % kind, lineno) elif kind == DoctestReviewer.SOURCE and length > 70: self._print_message('%s exceeds 78 characters.' % kind, lineno) else: # This line has a good length. pass return line def check_indentation(self, lineno, line, kind, previous_kind): """Check the indentation of the SOURCE or WANT line.""" if kind == DoctestReviewer.NARRATIVE: return line if self.example.indent != 4: if self.last_bad_indent != lineno - 1: self._print_message('%s has bad indentation.' % kind, lineno) self.last_bad_indent = lineno return line def check_trailing_whitespace(self, lineno, line, kind, previous_kind): """Check for the presence of trailing whitespace in the line.""" if line.endswith(' '): self._print_message('%s has trailing whitespace.' % kind, lineno) return line def check_heading(self, lineno, line, kind, previous_kind): """Check for narrative lines that use moin headers instead of RST.""" if kind != DoctestReviewer.NARRATIVE: return line moin = self.moin_pattern.match(line) if moin is not None: self._print_message('%s uses a moin header.' % kind, lineno) return line def check_source_code(self, code): """Check for source code problems in the doctest using pyflakes. The most common problem found are unused imports. `UndefinedName` errors are suppressed because the test setup is not known. """ if code == '': return try: tree = compile( code, self.file_path, "exec", _ast.PyCF_ONLY_AST) except (SyntaxError, IndentationError), exc: (lineno, offset_, line) = exc[1][1:] if line.endswith("\n"): line = line[:-1] self._print_message( 'Could not compile:\n %s' % line, lineno) else: warnings = Checker(tree) for warning in warnings.messages: if 'undefined name ' in str(warning): continue dummy, lineno, message = str(warning).split(':') self._print_message(message.strip(), lineno) def fix_trailing_whitespace(self, lineno, line, kind, previous_kind): """Return the line striped of trailing whitespace.""" return line.rstrip() def fix_indentation(self, lineno, line, kind, previous_kind): """set the indentation to 4-spaces.""" if kind == DoctestReviewer.NARRATIVE: return line elif kind == DoctestReviewer.WANT: return ' %s' % line else: if line.startswith(' '): # This is a continuation of DoctestReviewer.SOURCE. return ' ... %s' % line else: # This is a start of DoctestReviewer.SOURCE. return ' >>> %s' % line def fix_heading(self, lineno, line, kind, previous_kind): """Switch Moin headings to RST headings.""" if kind != DoctestReviewer.NARRATIVE: return line moin = self.moin_pattern.match(line) if moin is None: return line heading_level = len(moin.group(1)) heading = moin.group(2) rule_length = len(heading) if heading_level == 1: rule = '=' * rule_length elif heading_level == 2: rule = '-' * rule_length else: rule = '.' * rule_length # Force the heading on to the block of lines. self.block.append(heading) return rule def fix_narrative_paragraph(self, lineno, line, kind, previous_kind): """Break narrative into paragraphs.""" if kind != DoctestReviewer.NARRATIVE or len(self.block) == 0: return line if line == '': # This is the start of a new paragraph in the narrative. self._store_block(previous_kind) if self._is_formatted(line) and not self._is_formatted(self.block): # This line starts a pre-formatted paragraph. self._store_block(previous_kind) return line def format_and_save(self, is_interactive=False): new_doctest = self.format() if new_doctest != self.doctest: if is_interactive: diff = unified_diff( self.doctest.splitlines(), new_doctest.splitlines()) print '\n'.join(diff) print '\n' do_save = raw_input( 'Do you wish to save the changes? S(ave) or C(ancel)?') else: do_save = 'S' if do_save.upper() == 'S': with open(self.file_path, 'w') as doctest_file: doctest_file.write(new_doctest) self.doctest = new_doctest def get_option_parser(): """Return the option parser for this program.""" usage = "usage: %prog [options] doctest.txt" parser = OptionParser(usage=usage) parser.add_option( "-f", "--format", dest="is_format", action="store_true", help="Reformat the doctest.") parser.add_option( "-i", "--interactive", dest="is_interactive", action="store_true", help="Approve each change.") parser.set_defaults( is_format=False, is_interactive=False) return parser def main(argv=None): """Run the operations requested from the command line.""" if argv is None: argv = sys.argv parser = get_option_parser() (options, args) = parser.parse_args(args=argv[1:]) if len(args) == 0: parser.error("A doctest must be specified.") for file_path in args: with open(file_path) as doctest_file: doctest_data = doctest_file.read() reviewer = DoctestReviewer(file_path, doctest_data) if options.is_format: reviewer.format_and_save() reviewer.check() if __name__ == '__main__': sys.exit(main()) pocketlint-0.5.31/pocketlint/contrib/0000775000175000017500000000000011767132576020455 5ustar curtiscurtis00000000000000pocketlint-0.5.31/pocketlint/contrib/pyflakes/0000775000175000017500000000000011767132576022273 5ustar curtiscurtis00000000000000pocketlint-0.5.31/pocketlint/contrib/pyflakes/__init__.py0000644000175000017500000000002711700655773024375 0ustar curtiscurtis00000000000000 __version__ = '0.5.0' pocketlint-0.5.31/pocketlint/contrib/pyflakes/checker.py0000644000175000017500000005313211700660570024236 0ustar curtiscurtis00000000000000# -*- test-case-name: pyflakes -*- # (c) 2005-2010 Divmod, Inc. # See LICENSE file for details import __builtin__ import os.path import _ast # XXX sinzui 2012-01-03: Import the contrib version of message. #from pyflakes import messages from pocketlint.contrib.pyflakes import messages # utility function to iterate over an AST node's children, adapted # from Python 2.6's standard ast module try: import ast iter_child_nodes = ast.iter_child_nodes except (ImportError, AttributeError): def iter_child_nodes(node, astcls=_ast.AST): """ Yield all direct child nodes of *node*, that is, all fields that are nodes and all items of fields that are lists of nodes. """ for name in node._fields: field = getattr(node, name, None) if isinstance(field, astcls): yield field elif isinstance(field, list): for item in field: yield item class Binding(object): """ Represents the binding of a value to a name. The checker uses this to keep track of which names have been bound and which names have not. See L{Assignment} for a special type of binding that is checked with stricter rules. @ivar used: pair of (L{Scope}, line-number) indicating the scope and line number that this binding was last used """ def __init__(self, name, source): self.name = name self.source = source self.used = False def __str__(self): return self.name def __repr__(self): return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__, self.name, self.source.lineno, id(self)) class UnBinding(Binding): '''Created by the 'del' operator.''' class Importation(Binding): """ A binding created by an import statement. @ivar fullName: The complete name given to the import statement, possibly including multiple dotted components. @type fullName: C{str} """ def __init__(self, name, source): self.fullName = name name = name.split('.')[0] super(Importation, self).__init__(name, source) class Argument(Binding): """ Represents binding a name as an argument. """ class Assignment(Binding): """ Represents binding a name with an explicit assignment. The checker will raise warnings for any Assignment that isn't used. Also, the checker does not consider assignments in tuple/list unpacking to be Assignments, rather it treats them as simple Bindings. """ class FunctionDefinition(Binding): pass class ExportBinding(Binding): """ A binding created by an C{__all__} assignment. If the names in the list can be determined statically, they will be treated as names for export and additional checking applied to them. The only C{__all__} assignment that can be recognized is one which takes the value of a literal list containing literal strings. For example:: __all__ = ["foo", "bar"] Names which are imported and not otherwise used but appear in the value of C{__all__} will not have an unused import warning reported for them. """ def names(self): """ Return a list of the names referenced by this binding. """ names = [] if isinstance(self.source, _ast.List): for node in self.source.elts: if isinstance(node, _ast.Str): names.append(node.s) return names class Scope(dict): importStarred = False # set to True when import * is found def __repr__(self): return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) def __init__(self): super(Scope, self).__init__() class ClassScope(Scope): pass class FunctionScope(Scope): """ I represent a name scope for a function. @ivar globals: Names declared 'global' in this function. """ def __init__(self): super(FunctionScope, self).__init__() self.globals = {} class ModuleScope(Scope): pass # Globally defined names which are not attributes of the __builtin__ module. _MAGIC_GLOBALS = ['__file__', '__builtins__'] class Checker(object): """ I check the cleanliness and sanity of Python code. @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements of the list are two-tuples. The first element is the callable passed to L{deferFunction}. The second element is a copy of the scope stack at the time L{deferFunction} was called. @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for callables which are deferred assignment checks. """ nodeDepth = 0 traceTree = False def __init__(self, tree, filename='(none)'): self._deferredFunctions = [] self._deferredAssignments = [] self.dead_scopes = [] self.messages = [] self.filename = filename self.scopeStack = [ModuleScope()] self.futuresAllowed = True self.handleChildren(tree) self._runDeferred(self._deferredFunctions) # Set _deferredFunctions to None so that deferFunction will fail # noisily if called after we've run through the deferred functions. self._deferredFunctions = None self._runDeferred(self._deferredAssignments) # Set _deferredAssignments to None so that deferAssignment will fail # noisly if called after we've run through the deferred assignments. self._deferredAssignments = None del self.scopeStack[1:] self.popScope() self.check_dead_scopes() def deferFunction(self, callable): ''' Schedule a function handler to be called just before completion. This is used for handling function bodies, which must be deferred because code later in the file might modify the global scope. When `callable` is called, the scope at the time this is called will be restored, however it will contain any new bindings added to it. ''' self._deferredFunctions.append((callable, self.scopeStack[:])) def deferAssignment(self, callable): """ Schedule an assignment handler to be called just after deferred function handlers. """ self._deferredAssignments.append((callable, self.scopeStack[:])) def _runDeferred(self, deferred): """ Run the callables in C{deferred} using their associated scope stack. """ for handler, scope in deferred: self.scopeStack = scope handler() def scope(self): return self.scopeStack[-1] scope = property(scope) def popScope(self): self.dead_scopes.append(self.scopeStack.pop()) def check_dead_scopes(self): """ Look at scopes which have been fully examined and report names in them which were imported but unused. """ for scope in self.dead_scopes: export = isinstance(scope.get('__all__'), ExportBinding) if export: all = scope['__all__'].names() if os.path.split(self.filename)[1] != '__init__.py': # Look for possible mistakes in the export list undefined = set(all) - set(scope) for name in undefined: self.report( messages.UndefinedExport, scope['__all__'].source.lineno, name) else: all = [] # Look for imported names that aren't used. for importation in scope.itervalues(): if isinstance(importation, Importation): if not importation.used and importation.name not in all: self.report( messages.UnusedImport, importation.source.lineno, importation.name) def pushFunctionScope(self): self.scopeStack.append(FunctionScope()) def pushClassScope(self): self.scopeStack.append(ClassScope()) def report(self, messageClass, *args, **kwargs): self.messages.append(messageClass(self.filename, *args, **kwargs)) def handleChildren(self, tree): for node in iter_child_nodes(tree): self.handleNode(node, tree) def isDocstring(self, node): """ Determine if the given node is a docstring, as long as it is at the correct place in the node tree. """ return isinstance(node, _ast.Str) or \ (isinstance(node, _ast.Expr) and isinstance(node.value, _ast.Str)) def handleNode(self, node, parent): node.parent = parent if self.traceTree: print ' ' * self.nodeDepth + node.__class__.__name__ self.nodeDepth += 1 if self.futuresAllowed and not \ (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)): self.futuresAllowed = False nodeType = node.__class__.__name__.upper() try: handler = getattr(self, nodeType) handler(node) finally: self.nodeDepth -= 1 if self.traceTree: print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ def ignore(self, node): pass # "stmt" type nodes RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \ TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren CONTINUE = BREAK = PASS = ignore # "expr" type nodes BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \ CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren NUM = STR = ELLIPSIS = ignore # "slice" type nodes SLICE = EXTSLICE = INDEX = handleChildren # expression contexts are node instances too, though being constants LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore # same for operators AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore # additional node types COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren def addBinding(self, lineno, value, reportRedef=True): '''Called when a binding is altered. - `lineno` is the line of the statement responsible for the change - `value` is the optional new value, a Binding instance, associated with the binding; if None, the binding is deleted if it exists. - if `reportRedef` is True (default), rebinding while unused will be reported. ''' if (isinstance(self.scope.get(value.name), FunctionDefinition) and isinstance(value, FunctionDefinition)): self.report(messages.RedefinedFunction, lineno, value.name, self.scope[value.name].source.lineno) if not isinstance(self.scope, ClassScope): for scope in self.scopeStack[::-1]: existing = scope.get(value.name) if (isinstance(existing, Importation) and not existing.used and (not isinstance(value, Importation) or value.fullName == existing.fullName) and reportRedef): self.report(messages.RedefinedWhileUnused, lineno, value.name, scope[value.name].source.lineno) if isinstance(value, UnBinding): try: del self.scope[value.name] except KeyError: self.report(messages.UndefinedName, lineno, value.name) else: self.scope[value.name] = value def GLOBAL(self, node): """ Keep track of globals declarations. """ if isinstance(self.scope, FunctionScope): self.scope.globals.update(dict.fromkeys(node.names)) def LISTCOMP(self, node): # handle generators before element for gen in node.generators: self.handleNode(gen, node) self.handleNode(node.elt, node) GENERATOREXP = SETCOMP = LISTCOMP # dictionary comprehensions; introduced in Python 2.7 def DICTCOMP(self, node): for gen in node.generators: self.handleNode(gen, node) self.handleNode(node.key, node) self.handleNode(node.value, node) def FOR(self, node): """ Process bindings for loop variables. """ vars = [] def collectLoopVars(n): if isinstance(n, _ast.Name): vars.append(n.id) elif isinstance(n, _ast.expr_context): return else: for c in iter_child_nodes(n): collectLoopVars(c) collectLoopVars(node.target) for varn in vars: if (isinstance(self.scope.get(varn), Importation) # unused ones will get an unused import warning and self.scope[varn].used): self.report(messages.ImportShadowedByLoopVar, node.lineno, varn, self.scope[varn].source.lineno) self.handleChildren(node) def NAME(self, node): """ Handle occurrence of Name (which can be a load/store/delete access.) """ # Locate the name in locals / function / globals scopes. if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)): # try local scope importStarred = self.scope.importStarred try: self.scope[node.id].used = (self.scope, node.lineno) except KeyError: pass else: return # try enclosing function scopes for scope in self.scopeStack[-2:0:-1]: importStarred = importStarred or scope.importStarred if not isinstance(scope, FunctionScope): continue try: scope[node.id].used = (self.scope, node.lineno) except KeyError: pass else: return # try global scope importStarred = importStarred or self.scopeStack[0].importStarred try: self.scopeStack[0][node.id].used = (self.scope, node.lineno) except KeyError: if ((not hasattr(__builtin__, node.id)) and node.id not in _MAGIC_GLOBALS and not importStarred): if (os.path.basename(self.filename) == '__init__.py' and node.id == '__path__'): # the special name __path__ is valid only in packages pass else: self.report(messages.UndefinedName, node.lineno, node.id) elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)): # if the name hasn't already been defined in the current scope if isinstance(self.scope, FunctionScope) and node.id not in self.scope: # for each function or module scope above us for scope in self.scopeStack[:-1]: if not isinstance(scope, (FunctionScope, ModuleScope)): continue # if the name was defined in that scope, and the name has # been accessed already in the current scope, and hasn't # been declared global if (node.id in scope and scope[node.id].used and scope[node.id].used[0] is self.scope and node.id not in self.scope.globals): # then it's probably a mistake self.report(messages.UndefinedLocal, scope[node.id].used[1], node.id, scope[node.id].source.lineno) break if isinstance(node.parent, (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)): binding = Binding(node.id, node) elif (node.id == '__all__' and isinstance(self.scope, ModuleScope)): binding = ExportBinding(node.id, node.parent.value) else: binding = Assignment(node.id, node) if node.id in self.scope: binding.used = self.scope[node.id].used self.addBinding(node.lineno, binding) elif isinstance(node.ctx, _ast.Del): if isinstance(self.scope, FunctionScope) and \ node.id in self.scope.globals: del self.scope.globals[node.id] else: self.addBinding(node.lineno, UnBinding(node.id, node)) else: # must be a Param context -- this only happens for names in function # arguments, but these aren't dispatched through here raise RuntimeError( "Got impossible expression context: %r" % (node.ctx,)) def FUNCTIONDEF(self, node): # the decorators attribute is called decorator_list as of Python 2.6 if hasattr(node, 'decorators'): for deco in node.decorators: self.handleNode(deco, node) else: for deco in node.decorator_list: self.handleNode(deco, node) self.addBinding(node.lineno, FunctionDefinition(node.name, node)) self.LAMBDA(node) def LAMBDA(self, node): for default in node.args.defaults: self.handleNode(default, node) def runFunction(): args = [] def addArgs(arglist): for arg in arglist: if isinstance(arg, _ast.Tuple): addArgs(arg.elts) else: if arg.id in args: self.report(messages.DuplicateArgument, node.lineno, arg.id) args.append(arg.id) self.pushFunctionScope() addArgs(node.args.args) # vararg/kwarg identifiers are not Name nodes if node.args.vararg: args.append(node.args.vararg) if node.args.kwarg: args.append(node.args.kwarg) for name in args: self.addBinding(node.lineno, Argument(name, node), reportRedef=False) if isinstance(node.body, list): # case for FunctionDefs for stmt in node.body: self.handleNode(stmt, node) else: # case for Lambdas self.handleNode(node.body, node) def checkUnusedAssignments(): """ Check to see if any assignments have not been used. """ for name, binding in self.scope.iteritems(): if (not binding.used and not name in self.scope.globals and isinstance(binding, Assignment)): self.report(messages.UnusedVariable, binding.source.lineno, name) self.deferAssignment(checkUnusedAssignments) self.popScope() self.deferFunction(runFunction) def CLASSDEF(self, node): """ Check names used in a class definition, including its decorators, base classes, and the body of its definition. Additionally, add its name to the current scope. """ # decorator_list is present as of Python 2.6 for deco in getattr(node, 'decorator_list', []): self.handleNode(deco, node) for baseNode in node.bases: self.handleNode(baseNode, node) self.pushClassScope() for stmt in node.body: self.handleNode(stmt, node) self.popScope() self.addBinding(node.lineno, Binding(node.name, node)) def ASSIGN(self, node): self.handleNode(node.value, node) for target in node.targets: self.handleNode(target, node) def AUGASSIGN(self, node): # AugAssign is awkward: must set the context explicitly and visit twice, # once with AugLoad context, once with AugStore context node.target.ctx = _ast.AugLoad() self.handleNode(node.target, node) self.handleNode(node.value, node) node.target.ctx = _ast.AugStore() self.handleNode(node.target, node) def IMPORT(self, node): for alias in node.names: name = alias.asname or alias.name importation = Importation(name, node) self.addBinding(node.lineno, importation) def IMPORTFROM(self, node): if node.module == '__future__': if not self.futuresAllowed: self.report(messages.LateFutureImport, node.lineno, [n.name for n in node.names]) else: self.futuresAllowed = False for alias in node.names: if alias.name == '*': self.scope.importStarred = True self.report(messages.ImportStarUsed, node.lineno, node.module) continue name = alias.asname or alias.name importation = Importation(name, node) if node.module == '__future__': importation.used = (self.scope, node.lineno) self.addBinding(node.lineno, importation) pocketlint-0.5.31/pocketlint/contrib/pyflakes/messages.py0000644000175000017500000000576011700655773024456 0ustar curtiscurtis00000000000000# (c) 2005 Divmod, Inc. See LICENSE file for details class Message(object): message = '' message_args = () def __init__(self, filename, lineno): self.filename = filename self.lineno = lineno def __str__(self): return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args) class UnusedImport(Message): message = '%r imported but unused' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class RedefinedWhileUnused(Message): message = 'redefinition of unused %r from line %r' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class ImportShadowedByLoopVar(Message): message = 'import %r from line %r shadowed by loop variable' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class ImportStarUsed(Message): message = "'from %s import *' used; unable to detect undefined names" def __init__(self, filename, lineno, modname): Message.__init__(self, filename, lineno) self.message_args = (modname,) class UndefinedName(Message): message = 'undefined name %r' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class UndefinedExport(Message): message = 'undefined name %r in __all__' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class UndefinedLocal(Message): message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment" def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class DuplicateArgument(Message): message = 'duplicate argument %r in function definition' def __init__(self, filename, lineno, name): Message.__init__(self, filename, lineno) self.message_args = (name,) class RedefinedFunction(Message): message = 'redefinition of function %r from line %r' def __init__(self, filename, lineno, name, orig_lineno): Message.__init__(self, filename, lineno) self.message_args = (name, orig_lineno) class LateFutureImport(Message): message = 'future import(s) %r after other statements' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names,) class UnusedVariable(Message): """ Indicates that a variable has been explicity assigned to but not actually used. """ message = 'local variable %r is assigned to but never used' def __init__(self, filename, lineno, names): Message.__init__(self, filename, lineno) self.message_args = (names,) pocketlint-0.5.31/pocketlint/contrib/__init__.py0000644000175000017500000000012311412422154022535 0ustar curtiscurtis00000000000000# Copyright (C) 2010 Curtis Hovey # GPL 3, see COPYING. pocketlint-0.5.31/pocketlint/contrib/fulljslint.js0000644000175000017500000072250211543264332023173 0ustar curtiscurtis00000000000000// jslint.js // 2011-03-22 // Copyright (c) 2002 Douglas Crockford (www.JSLint.com) // 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 shall be used for Good, not Evil. // 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. // JSLINT is a global function. It takes two parameters. // var myResult = JSLINT(source, option); // The first parameter is either a string or an array of strings. If it is a // string, it will be split on '\n' or '\r'. If it is an array of strings, it // is assumed that each string represents one line. The source can be a // JavaScript text, or HTML text, or a JSON text, or a CSS text. // The second parameter is an optional object of options that control the // operation of JSLINT. Most of the options are booleans: They are all // optional and have a default value of false. One of the options, predef, // can be an array of names, which will be used to declare global variables, // or an object whose keys are used as global names, with a boolean value // that determines if they are assignable. // If it checks out, JSLINT returns true. Otherwise, it returns false. // If false, you can inspect JSLINT.errors to find out the problems. // JSLINT.errors is an array of objects containing these properties: // { // line : The line (relative to 0) at which the lint was found // character : The character (relative to 0) at which the lint was found // reason : The problem // evidence : The text line in which the problem occurred // raw : The raw message before the details were inserted // a : The first detail // b : The second detail // c : The third detail // d : The fourth detail // } // If a stopping error was found, a null will be the last element of the // JSLINT.errors array. A stopping error means that JSLint was not confident // enough to continue. It does not necessarily mean that the error was // especailly heinous. // You can request a Function Report, which shows all of the functions // and the parameters and vars that they use. This can be used to find // implied global variables and other problems. The report is in HTML and // can be inserted in an HTML . // var myReport = JSLINT.report(errors_only); // If errors_only is true, then the report will be limited to only errors. // You can request a data structure that contains JSLint's results. // var myData = JSLINT.data(); // It returns a structure with this form: // { // errors: [ // { // line: NUMBER, // character: NUMBER, // reason: STRING, // evidence: STRING // } // ], // functions: [ // name: STRING, // line: NUMBER, // last: NUMBER, // param: [ // TOKEN // ], // closure: [ // STRING // ], // var: [ // STRING // ], // exception: [ // STRING // ], // outer: [ // STRING // ], // unused: [ // STRING // ], // global: [ // STRING // ], // label: [ // STRING // ] // ], // globals: [ // STRING // ], // member: { // STRING: NUMBER // }, // unuseds: [ // { // name: STRING, // line: NUMBER // } // ], // implieds: [ // { // name: STRING, // line: NUMBER // } // ], // urls: [ // STRING // ], // json: BOOLEAN // } // Empty arrays will not be included. // You can obtain the parse tree that JSLint constructed while parsing. The // latest tree is kept in JSLINT.tree. A nice stringication can be produced // with // JSON.stringify(JSLINT.tree, [ // 'value', 'arity', 'name', 'first', // 'second', 'third', 'block', 'else' // ], 4)); // JSLint provides three directives. They look like slashstar comments, and // allow for setting options, declaring global variables, and establishing a // set of allowed property names. // These directives respect function scope. // The jslint directive is a special comment that can set one or more options. // The current option set is // adsafe true, if ADsafe rules should be enforced // bitwise true, if bitwise operators should not be allowed // browser true, if the standard browser globals should be predefined // cap true, if upper case HTML should be allowed // 'continue' true, if the continuation statement should be tolerated // css true, if CSS workarounds should be tolerated // debug true, if debugger statements should be allowed // devel true, if logging should be allowed (console, alert, etc.) // es5 true, if ES5 syntax should be allowed // evil true, if eval should be allowed // forin true, if for in statements need not filter // fragment true, if HTML fragments should be allowed // indent the indentation factor // maxerr the maximum number of errors to allow // maxlen the maximum length of a source line // newcap true, if constructor names must be capitalized // node true, if Node.js globals should be predefined // nomen true, if names should be checked // on true, if HTML event handlers should be allowed // onevar true, if only one var statement per function should be allowed // passfail true, if the scan should stop on first error // plusplus true, if increment/decrement should not be allowed // regexp true, if the . should not be allowed in regexp literals // rhino true, if the Rhino environment globals should be predefined // undef true, if variables should be declared before used // safe true, if use of some browser features should be restricted // windows true, if MS Windows-specific globals should be predefined // strict true, require the "use strict"; pragma // sub true, if all forms of subscript notation are tolerated // white true, if strict whitespace rules apply // widget true if the Yahoo Widgets globals should be predefined // For example: /*jslint evil: true, nomen: false, onevar: false, regexp: false, strict: true */ // The properties directive declares an exclusive list of property names. // Any properties named in the program that are not in the list will // produce a warning. // For example: /*properties "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "'", "(begin)", "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(token)", "(verb)", ")", "*", "+", "-", "\/", ";", "<", "<=", "==", "===", ">", ">=", ADSAFE, ActiveXObject, Array, Boolean, COM, CScript, Canvas, CustomAnimation, Date, Debug, E, Enumerator, Error, EvalError, FadeAnimation, Flash, FormField, Frame, Function, HotKey, Image, JSON, LN10, LN2, LOG10E, LOG2E, MAX_VALUE, MIN_VALUE, Math, MenuItem, MoveAnimation, NEGATIVE_INFINITY, Number, Object, Option, PI, POSITIVE_INFINITY, Point, RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, RotateAnimation, SQRT1_2, SQRT2, ScrollBar, String, Style, SyntaxError, System, Text, TextArea, Timer, TypeError, URIError, URL, VBArray, WScript, Web, Window, XMLDOM, XMLHttpRequest, "\\", __dirname, __filename, a, a_function, a_label, a_not_allowed, a_not_defined, a_scope, abbr, acronym, activeborder, activecaption, address, adsafe, adsafe_a, adsafe_autocomplete, adsafe_bad_id, adsafe_div, adsafe_fragment, adsafe_go, adsafe_html, adsafe_id, adsafe_id_go, adsafe_lib, adsafe_lib_second, adsafe_missing_id, adsafe_name_a, adsafe_placement, adsafe_prefix_a, adsafe_script, adsafe_source, adsafe_subscript_a, adsafe_tag, alert, aliceblue, all, already_defined, and, animator, antiquewhite, appleScript, applet, apply, approved, appworkspace, aqua, aquamarine, area, arguments, arity, article, aside, assign, assign_exception, assignment_function_expression, at, attribute_case_a, audio, autocomplete, avoid_a, azure, b, background, "background-attachment", "background-color", "background-image", "background-position", "background-repeat", bad_assignment, bad_color_a, bad_constructor, bad_entity, bad_html, bad_id_a, bad_in_a, bad_invocation, bad_name_a, bad_new, bad_number, bad_operand, bad_type, bad_url, bad_wrap, base, bdo, beep, beige, big, bisque, bitwise, black, blanchedalmond, block, blockquote, blue, blueviolet, body, border, "border-bottom", "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-collapse", "border-color", "border-left", "border-left-color", "border-left-style", "border-left-width", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-spacing", "border-style", "border-top", "border-top-color", "border-top-style", "border-top-width", "border-width", bottom, br, braille, brown, browser, burlywood, button, buttonface, buttonhighlight, buttonshadow, buttontext, bytesToUIString, c, cadetblue, call, callee, caller, canvas, cap, caption, "caption-side", captiontext, center, charAt, charCodeAt, character, chartreuse, chocolate, chooseColor, chooseFile, chooseFolder, cite, clear, clearInterval, clearTimeout, clip, closeWidget, closure, cm, code, col, colgroup, color, combine_var, command, comment, comments, concat, conditional_assignment, confirm, confusing_a, confusing_regexp, console, constructor, constructor_name_a, content, continue, control_a, convertPathToHFS, convertPathToPlatform, coral, cornflowerblue, cornsilk, "counter-increment", "counter-reset", create, crimson, css, cursor, cyan, d, dangerous_comment, dangling_a, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen, darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred, darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise, darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent, deeppink, deepskyblue, defineClass, del, deleted, deserialize, details, devel, dfn, dialog, dimgray, dir, direction, display, disrupt, div, dl, document, dodgerblue, dt, duplicate_a, edge, edition, else, em, embed, embossed, empty, "empty-cells", empty_block, empty_case, empty_class, encodeURI, encodeURIComponent, entityify, errors, es5, escape, eval, event, evidence, evil, ex, exception, exec, expected_a, expected_a_at_b_c, expected_a_b, expected_a_b_from_c_d, expected_at_a, expected_attribute_a, expected_attribute_value_a, expected_class_a, expected_fraction_a, expected_id_a, expected_identifier_a, expected_identifier_a_reserved, expected_lang_a, expected_linear_a, expected_media_a, expected_name_a, expected_nonstandard_style_attribute, expected_number_a, expected_operator_a, expected_percent_a, expected_positive_a, expected_pseudo_a, expected_selector_a, expected_small_a, expected_space_a_b, expected_string_a, expected_style_attribute, expected_style_pattern, expected_tagname_a, fieldset, figure, filesystem, filter, firebrick, first, float, floor, floralwhite, focusWidget, font, "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", footer, for_if, forestgreen, forin, form, fragment, frame, frames, frameset, from, fromCharCode, fuchsia, fud, funct, function, function_block, function_eval, function_loop, function_statement, function_strict, functions, g, gainsboro, gc, get_set, ghostwhite, global, globals, gold, goldenrod, gray, graytext, green, greenyellow, h1, h2, h3, h4, h5, h6, handheld, hasOwnProperty, head, header, height, help, hgroup, highlight, highlighttext, history, honeydew, hotpink, hr, "hta:application", html, html_confusion_a, html_handlers, i, iTunes, id, identifier, identifier_function, iframe, img, immed, implied_evil, implieds, in, inactiveborder, inactivecaption, inactivecaptiontext, include, indent, indexOf, indianred, indigo, infix_in, infobackground, infotext, init, input, ins, insecure_a, isAlpha, isApplicationRunning, isArray, isDigit, isFinite, isNaN, ivory, join, jslint, json, kbd, keygen, keys, khaki, konfabulatorVersion, label, label_a_b, labeled, lang, lavender, lavenderblush, lawngreen, lbp, leading_decimal_a, led, left, legend, lemonchiffon, length, "letter-spacing", li, lib, lightblue, lightcoral, lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon, lightseagreen, lightskyblue, lightslategray, lightsteelblue, lightyellow, lime, limegreen, line, "line-height", linen, link, "list-style", "list-style-image", "list-style-position", "list-style-type", load, loadClass, location, log, m, magenta, map, margin, "margin-bottom", "margin-left", "margin-right", "margin-top", mark, "marker-offset", maroon, match, "max-height", "max-width", maxerr, maxlen, md5, mediumaquamarine, mediumblue, mediumorchid, mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen, mediumturquoise, mediumvioletred, member, menu, menutext, message, meta, meter, midnightblue, "min-height", "min-width", mintcream, missing_a, missing_a_after_b, missing_option, missing_property, missing_space_a_b, missing_url, missing_use_strict, mistyrose, mixed, mm, moccasin, mode, module, move_invocation, move_var, name, name_function, nav, navajowhite, navigator, navy, nested_comment, newcap, next, node, noframes, nomen, noscript, not, not_a_constructor, not_a_defined, not_a_function, not_a_label, not_a_scope, not_greater, nud, object, ol, oldlace, olive, olivedrab, on, onevar, opacity, open, openURL, opera, optgroup, option, orange, orangered, orchid, outer, outline, "outline-color", "outline-style", "outline-width", output, overflow, "overflow-x", "overflow-y", p, padding, "padding-bottom", "padding-left", "padding-right", "padding-top", "page-break-after", "page-break-before", palegoldenrod, palegreen, paleturquoise, palevioletred, papayawhip, param, parameter_a_get_b, parameter_set_a, paren, parent, parseFloat, parseInt, passfail, pc, peachpuff, peru, pink, play, plum, plusplus, pop, popupMenu, position, postscript, powderblue, pre, predef, preferenceGroups, preferences, prev, print, process, progress, projection, prompt, prototype, pt, purple, push, px, q, quit, quote, quotes, radix, random, range, raw, readFile, readUrl, read_only, reason, red, redefinition_a, regexp, reloadWidget, replace, report, require, reserved, reserved_a, resolvePath, resumeUpdates, rhino, right, rosybrown, royalblue, rp, rt, ruby, runCommand, runCommandInBg, saddlebrown, safe, salmon, samp, sandybrown, saveAs, savePreferences, scanned_a_b, screen, script, scrollbar, seagreen, seal, search, seashell, second, section, select, serialize, setInterval, setTimeout, shift, showWidgetPreferences, sienna, silver, skyblue, slash_equal, slateblue, slategray, sleep, slice, small, snow, sort, source, span, spawn, speak, speech, split, springgreen, src, stack, statement_block, steelblue, stopping, strange_loop, strict, strong, style, styleproperty, sub, subscript, substr, sup, supplant, suppressUpdates, sync, system, table, "table-layout", tag_a_in_b, tan, tbody, td, teal, tellWidget, test, "text-align", "text-decoration", "text-indent", "text-shadow", "text-transform", textarea, tfoot, th, thead, third, thistle, threeddarkshadow, threedface, threedhighlight, threedlightshadow, threedshadow, thru, time, title, toLowerCase, toString, toUpperCase, toint32, token, tomato, too_long, too_many, top, tr, trailing_decimal_a, tree, tt, tty, turquoise, tv, type, u, ul, unclosed, unclosed_comment, unclosed_regexp, undef, unescape, unescaped_a, unexpected_a, unexpected_char_a_b, unexpected_comment, unexpected_member_a, unexpected_space_a_b, "unicode-bidi", unnecessary_initialize, unnecessary_use, unreachable_a_b, unrecognized_style_attribute_a, unrecognized_tag_a, unsafe, unused, unwatch, updateNow, url, urls, use_array, use_braces, use_object, used_before_a, value, valueOf, var, var_a_not, version, "vertical-align", video, violet, visibility, was, watch, weird_assignment, weird_condition, weird_new, weird_program, weird_relation, weird_ternary, wheat, white, "white-space", whitesmoke, widget, width, window, windowframe, windows, windowtext, "word-spacing", "word-wrap", wrap, wrap_immediate, wrap_regexp, write_is_wrong, yahooCheckLogin, yahooLogin, yahooLogout, yellow, yellowgreen, "z-index" */ // The global directive is used to declare global variables that can // be accessed by the program. If a declaration is true, then the variable // is writeable. Otherwise, it is read-only. // We build the application inside a function so that we produce only a single // global variable. That function will be invoked immediately, and its return // value is the JSLINT function itself. That function is also an object that // can contain data and other functions. var JSLINT = (function () { "use strict"; var adsafe_id, // The widget's ADsafe id. adsafe_may, // The widget may load approved scripts. adsafe_top, // At the top of the widget script. adsafe_went, // ADSAFE.go has been called. anonname, // The guessed name for anonymous functions. approved, // ADsafe approved urls. // These are operators that should not be used with the ! operator. bang = { '<' : true, '<=' : true, '==' : true, '===': true, '!==': true, '!=' : true, '>' : true, '>=' : true, '+' : true, '-' : true, '*' : true, '/' : true, '%' : true }, // These are property names that should not be permitted in the safe subset. banned = { 'arguments' : true, callee : true, caller : true, constructor : true, 'eval' : true, prototype : true, stack : true, unwatch : true, valueOf : true, watch : true }, begin, // The root token // browser contains a set of global names that are commonly provided by a // web browser environment. self and window are intentially excluded because // of the high likelihood for misue. browser = { clearInterval : false, clearTimeout : false, document : false, event : false, frames : false, history : false, Image : false, location : false, name : false, navigator : false, Option : false, parent : false, screen : false, setInterval : false, setTimeout : false, XMLHttpRequest : false }, // bundle contains the text messages. bundle = { a_function: "'{a}' is a function.", a_label: "'{a}' is a statement label.", a_not_allowed: "'{a}' is not allowed.", a_not_defined: "'{a}' is not defined.", a_scope: "'{a}' used out of scope.", adsafe: "ADsafe violation.", adsafe_a: "ADsafe violation: '{a}'.", adsafe_autocomplete: "ADsafe autocomplete violation.", adsafe_bad_id: "ADSAFE violation: bad id.", adsafe_div: "ADsafe violation: Wrap the widget in a div.", adsafe_fragment: "ADSAFE: Use the fragment option.", adsafe_go: "ADsafe violation: Missing ADSAFE.go.", adsafe_html: "Currently, ADsafe does not operate on whole HTML documents. It operates on
fragments and .js files.", adsafe_id: "ADsafe violation: id does not match.", adsafe_id_go: "ADsafe violation: Missing ADSAFE.id or ADSAFE.go.", adsafe_lib: "ADsafe lib violation.", adsafe_lib_second: "ADsafe: The second argument to lib must be a function.", adsafe_missing_id: "ADSAFE violation: missing ID_.", adsafe_name_a: "ADsafe name violation: '{a}'.", adsafe_placement: "ADsafe script placement violation.", adsafe_prefix_a: "ADsafe violation: An id must have a '{a}' prefix", adsafe_script: "ADsafe script violation.", adsafe_source: "ADsafe unapproved script source.", adsafe_subscript_a: "ADsafe subscript '{a}'.", adsafe_tag: "ADsafe violation: Disallowed tag '{a}'.", already_defined: "'{a}' is already defined.", and: "The '&&' subexpression should be wrapped in parens.", assign_exception: "Do not assign to the exception parameter.", assignment_function_expression: "Expected an assignment or function call and instead saw an expression.", attribute_case_a: "Attribute '{a}' not all lower case.", avoid_a: "Avoid '{a}'.", bad_assignment: "Bad assignment.", bad_color_a: "Bad hex color '{a}'.", bad_constructor: "Bad constructor.", bad_entity: "Bad entity.", bad_html: "Bad HTML string", bad_id_a: "Bad id: '{a}'.", bad_in_a: "Bad for in variable '{a}'.", bad_invocation: "Bad invocation.", bad_name_a: "Bad name: '{a}'.", bad_new: "Do not use 'new' for side effects.", bad_number: "Bad number '{a}'.", bad_operand: "Bad operand.", bad_type: "Bad type.", bad_url: "Bad url string.", bad_wrap: "Do not wrap function literals in parens unless they are to be immediately invoked.", combine_var: "Combine this with the previous 'var' statement.", conditional_assignment: "Expected a conditional expression and instead saw an assignment.", confusing_a: "Confusing use of '{a}'.", confusing_regexp: "Confusing regular expression.", constructor_name_a: "A constructor name '{a}' should start with an uppercase letter.", control_a: "Unexpected control character '{a}'.", css: "A css file should begin with @charset 'UTF-8';", dangling_a: "Unexpected dangling '_' in '{a}'.", dangerous_comment: "Dangerous comment.", deleted: "Only properties should be deleted.", duplicate_a: "Duplicate '{a}'.", empty_block: "Empty block.", empty_case: "Empty case.", empty_class: "Empty class.", evil: "eval is evil.", expected_a: "Expected '{a}'.", expected_a_b: "Expected '{a}' and instead saw '{b}'.", expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", expected_at_a: "Expected an at-rule, and instead saw @{a}.", expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", expected_attribute_a: "Expected an attribute, and instead saw [{a}].", expected_attribute_value_a: "Expected an attribute value and instead saw '{a}'.", expected_class_a: "Expected a class, and instead saw .{a}.", expected_fraction_a: "Expected a number between 0 and 1 and instead saw '{a}'", expected_id_a: "Expected an id, and instead saw #{a}.", expected_identifier_a: "Expected an identifier and instead saw '{a}'.", expected_identifier_a_reserved: "Expected an identifier and instead saw '{a}' (a reserved word).", expected_linear_a: "Expected a linear unit and instead saw '{a}'.", expected_lang_a: "Expected a lang code, and instead saw :{a}.", expected_media_a: "Expected a CSS media type, and instead saw '{a}'.", expected_name_a: "Expected a name and instead saw '{a}'.", expected_nonstandard_style_attribute: "Expected a non-standard style attribute and instead saw '{a}'.", expected_number_a: "Expected a number and instead saw '{a}'.", expected_operator_a: "Expected an operator and instead saw '{a}'.", expected_percent_a: "Expected a percentage and instead saw '{a}'", expected_positive_a: "Expected a positive number and instead saw '{a}'", expected_pseudo_a: "Expected a pseudo, and instead saw :{a}.", expected_selector_a: "Expected a CSS selector, and instead saw {a}.", expected_small_a: "Expected a small number and instead saw '{a}'", expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", expected_string_a: "Expected a string and instead saw {a}.", expected_style_attribute: "Excepted a style attribute, and instead saw '{a}'.", expected_style_pattern: "Expected a style pattern, and instead saw '{a}'.", expected_tagname_a: "Expected a tagName, and instead saw {a}.", for_if: "The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", function_block: "Function statements should not be placed in blocks. " + "Use a function expression or move the statement to the top of " + "the outer function.", function_eval: "The Function constructor is eval.", function_loop: "Don't make functions within a loop.", function_statement: "Function statements are not invocable. " + "Wrap the whole function invocation in parens.", function_strict: "Use the function form of \"use strict\".", get_set: "get/set are ES5 features.", html_confusion_a: "HTML confusion in regular expression '<{a}'.", html_handlers: "Avoid HTML event handlers.", identifier_function: "Expected an identifier in an assignment and instead saw a function invocation.", implied_evil: "Implied eval is evil. Pass a function instead of a string.", infix_in: "Unexpected 'in'. Compare with undefined, or use the hasOwnProperty method instead.", insecure_a: "Insecure '{a}'.", isNaN: "Use the isNaN function to compare with NaN.", label_a_b: "Label '{a}' on '{b}' statement.", lang: "lang is deprecated.", leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", missing_a: "Missing '{a}'.", missing_a_after_b: "Missing '{a}' after '{b}'.", missing_option: "Missing option value.", missing_property: "Missing property name.", missing_space_a_b: "Missing space between '{a}' and '{b}'.", missing_url: "Missing url.", missing_use_strict: "Missing \"use strict\" statement.", mixed: "Mixed spaces and tabs.", move_invocation: "Move the invocation into the parens that contain the function.", move_var: "Move 'var' declarations to the top of the function.", name_function: "Missing name in function statement.", nested_comment: "Nested comment.", not: "Nested not.", not_a_constructor: "Do not use {a} as a constructor.", not_a_defined: "'{a}' has not been fully defined yet.", not_a_function: "'{a}' is not a function.", not_a_label: "'{a}' is not a label.", not_a_scope: "'{a}' is out of scope.", not_greater: "'{a}' should not be greater than '{b}'.", parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", parameter_set_a: "Expected parameter (value) in set {a} function.", radix: "Missing radix parameter.", read_only: "Read only.", redefinition_a: "Redefinition of '{a}'.", reserved_a: "Reserved name '{a}'.", scanned_a_b: "{a} ({b}% scanned).", slash_equal: "A regular expression literal can be confused with '/='.", statement_block: "Expected to see a statement and instead saw a block.", stopping: "Stopping. ", strange_loop: "Strange loop.", strict: "Strict violation.", subscript: "['{a}'] is better written in dot notation.", tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", too_long: "Line too long.", too_many: "Too many errors.", trailing_decimal_a: "A trailing decimal point can be confused with a dot: '.{a}'.", type: "type is unnecessary.", unclosed: "Unclosed string.", unclosed_comment: "Unclosed comment.", unclosed_regexp: "Unclosed regular expression.", unescaped_a: "Unescaped '{a}'.", unexpected_a: "Unexpected '{a}'.", unexpected_char_a_b: "Unexpected character '{a}' in {b}.", unexpected_comment: "Unexpected comment.", unexpected_member_a: "Unexpected property '{a}'.", unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", unnecessary_initialize: "It is not necessary to initialize '{a}' to 'undefined'.", unnecessary_use: "Unnecessary \"use strict\".", unreachable_a_b: "Unreachable '{a}' after '{b}'.", unrecognized_style_attribute_a: "Unrecognized style attribute '{a}'.", unrecognized_tag_a: "Unrecognized tag '<{a}>'.", unsafe: "Unsafe character.", url: "JavaScript URL.", use_array: "Use the array literal notation [].", use_braces: "Spaces are hard to count. Use {{a}}.", use_object: "Use the object literal notation {}.", used_before_a: "'{a}' was used before it was defined.", var_a_not: "Variable {a} was not declared correctly.", weird_assignment: "Weird assignment.", weird_condition: "Weird condition.", weird_new: "Weird construction. Delete 'new'.", weird_program: "Weird program.", weird_relation: "Weird relation.", weird_ternary: "Weird ternary.", wrap_immediate: "Wrap an immediate function invocation in parentheses " + "to assist the reader in understanding that the expression " + "is the result of a function, and not the function itself.", wrap_regexp: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.", write_is_wrong: "document.write can be a form of eval." }, comments_off, css_attribute_data, css_any, css_colorData = { "aliceblue" : true, "antiquewhite" : true, "aqua" : true, "aquamarine" : true, "azure" : true, "beige" : true, "bisque" : true, "black" : true, "blanchedalmond" : true, "blue" : true, "blueviolet" : true, "brown" : true, "burlywood" : true, "cadetblue" : true, "chartreuse" : true, "chocolate" : true, "coral" : true, "cornflowerblue" : true, "cornsilk" : true, "crimson" : true, "cyan" : true, "darkblue" : true, "darkcyan" : true, "darkgoldenrod" : true, "darkgray" : true, "darkgreen" : true, "darkkhaki" : true, "darkmagenta" : true, "darkolivegreen" : true, "darkorange" : true, "darkorchid" : true, "darkred" : true, "darksalmon" : true, "darkseagreen" : true, "darkslateblue" : true, "darkslategray" : true, "darkturquoise" : true, "darkviolet" : true, "deeppink" : true, "deepskyblue" : true, "dimgray" : true, "dodgerblue" : true, "firebrick" : true, "floralwhite" : true, "forestgreen" : true, "fuchsia" : true, "gainsboro" : true, "ghostwhite" : true, "gold" : true, "goldenrod" : true, "gray" : true, "green" : true, "greenyellow" : true, "honeydew" : true, "hotpink" : true, "indianred" : true, "indigo" : true, "ivory" : true, "khaki" : true, "lavender" : true, "lavenderblush" : true, "lawngreen" : true, "lemonchiffon" : true, "lightblue" : true, "lightcoral" : true, "lightcyan" : true, "lightgoldenrodyellow" : true, "lightgreen" : true, "lightpink" : true, "lightsalmon" : true, "lightseagreen" : true, "lightskyblue" : true, "lightslategray" : true, "lightsteelblue" : true, "lightyellow" : true, "lime" : true, "limegreen" : true, "linen" : true, "magenta" : true, "maroon" : true, "mediumaquamarine" : true, "mediumblue" : true, "mediumorchid" : true, "mediumpurple" : true, "mediumseagreen" : true, "mediumslateblue" : true, "mediumspringgreen" : true, "mediumturquoise" : true, "mediumvioletred" : true, "midnightblue" : true, "mintcream" : true, "mistyrose" : true, "moccasin" : true, "navajowhite" : true, "navy" : true, "oldlace" : true, "olive" : true, "olivedrab" : true, "orange" : true, "orangered" : true, "orchid" : true, "palegoldenrod" : true, "palegreen" : true, "paleturquoise" : true, "palevioletred" : true, "papayawhip" : true, "peachpuff" : true, "peru" : true, "pink" : true, "plum" : true, "powderblue" : true, "purple" : true, "red" : true, "rosybrown" : true, "royalblue" : true, "saddlebrown" : true, "salmon" : true, "sandybrown" : true, "seagreen" : true, "seashell" : true, "sienna" : true, "silver" : true, "skyblue" : true, "slateblue" : true, "slategray" : true, "snow" : true, "springgreen" : true, "steelblue" : true, "tan" : true, "teal" : true, "thistle" : true, "tomato" : true, "turquoise" : true, "violet" : true, "wheat" : true, "white" : true, "whitesmoke" : true, "yellow" : true, "yellowgreen" : true, "activeborder" : true, "activecaption" : true, "appworkspace" : true, "background" : true, "buttonface" : true, "buttonhighlight" : true, "buttonshadow" : true, "buttontext" : true, "captiontext" : true, "graytext" : true, "highlight" : true, "highlighttext" : true, "inactiveborder" : true, "inactivecaption" : true, "inactivecaptiontext" : true, "infobackground" : true, "infotext" : true, "menu" : true, "menutext" : true, "scrollbar" : true, "threeddarkshadow" : true, "threedface" : true, "threedhighlight" : true, "threedlightshadow" : true, "threedshadow" : true, "window" : true, "windowframe" : true, "windowtext" : true }, css_border_style, css_break, css_lengthData = { '%': true, 'cm': true, 'em': true, 'ex': true, 'in': true, 'mm': true, 'pc': true, 'pt': true, 'px': true }, css_media, css_overflow, devel = { alert : false, confirm : false, console : false, Debug : false, opera : false, prompt : false }, escapes = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '/' : '\\/', '\\': '\\\\' }, funct, // The current function functionicity = [ 'closure', 'exception', 'global', 'label', 'outer', 'unused', 'var' ], functions, // All of the functions global, // The global scope html_tag = { a: {}, abbr: {}, acronym: {}, address: {}, applet: {}, area: {empty: true, parent: ' map '}, article: {}, aside: {}, audio: {}, b: {}, base: {empty: true, parent: ' head '}, bdo: {}, big: {}, blockquote: {}, body: {parent: ' html noframes '}, br: {empty: true}, button: {}, canvas: {parent: ' body p div th td '}, caption: {parent: ' table '}, center: {}, cite: {}, code: {}, col: {empty: true, parent: ' table colgroup '}, colgroup: {parent: ' table '}, command: {parent: ' menu '}, datalist: {}, dd: {parent: ' dl '}, del: {}, details: {}, dialog: {}, dfn: {}, dir: {}, div: {}, dl: {}, dt: {parent: ' dl '}, em: {}, embed: {}, fieldset: {}, figure: {}, font: {}, footer: {}, form: {}, frame: {empty: true, parent: ' frameset '}, frameset: {parent: ' html frameset '}, h1: {}, h2: {}, h3: {}, h4: {}, h5: {}, h6: {}, head: {parent: ' html '}, header: {}, hgroup: {}, hr: {empty: true}, 'hta:application': {empty: true, parent: ' head '}, html: {parent: '*'}, i: {}, iframe: {}, img: {empty: true}, input: {empty: true}, ins: {}, kbd: {}, keygen: {}, label: {}, legend: {parent: ' details fieldset figure '}, li: {parent: ' dir menu ol ul '}, link: {empty: true, parent: ' head '}, map: {}, mark: {}, menu: {}, meta: {empty: true, parent: ' head noframes noscript '}, meter: {}, nav: {}, noframes: {parent: ' html body '}, noscript: {parent: ' body head noframes '}, object: {}, ol: {}, optgroup: {parent: ' select '}, option: {parent: ' optgroup select '}, output: {}, p: {}, param: {empty: true, parent: ' applet object '}, pre: {}, progress: {}, q: {}, rp: {}, rt: {}, ruby: {}, samp: {}, script: {empty: true, parent: ' body div frame head iframe p pre span '}, section: {}, select: {}, small: {}, span: {}, source: {}, strong: {}, style: {parent: ' head ', empty: true}, sub: {}, sup: {}, table: {}, tbody: {parent: ' table '}, td: {parent: ' tr '}, textarea: {}, tfoot: {parent: ' table '}, th: {parent: ' tr '}, thead: {parent: ' table '}, time: {}, title: {parent: ' head '}, tr: {parent: ' table tbody thead tfoot '}, tt: {}, u: {}, ul: {}, 'var': {}, video: {} }, ids, // HTML ids implied, // Implied globals in_block, indent, json_mode, lines, lookahead, member, node = { global : true, module : true, process : true, require : true, __filename : true, __dirname : true }, properties, next_token, older_token, option, predefined, // Global variables defined by option prereg, prev_token, regexp_flag = { g: true, i: true, m: true }, rhino = { defineClass : false, deserialize : false, gc : false, help : false, load : false, loadClass : false, print : false, quit : false, readFile : false, readUrl : false, runCommand : false, seal : false, serialize : false, spawn : false, sync : false, toint32 : false, version : false }, scope, // The current scope semicolon_coda = { ';' : true, '"' : true, '\'': true, ')' : true }, src, stack, // standard contains the global names that are provided by the // ECMAScript standard. standard = { Array : false, Boolean : false, Date : false, decodeURI : false, decodeURIComponent : false, encodeURI : false, encodeURIComponent : false, Error : false, 'eval' : false, EvalError : false, Function : false, hasOwnProperty : false, isFinite : false, isNaN : false, JSON : false, Math : false, Number : false, Object : false, parseInt : false, parseFloat : false, RangeError : false, ReferenceError : false, RegExp : false, String : false, SyntaxError : false, TypeError : false, URIError : false }, standard_property = { E : true, LN2 : true, LN10 : true, LOG2E : true, LOG10E : true, MAX_VALUE : true, MIN_VALUE : true, NEGATIVE_INFINITY : true, PI : true, POSITIVE_INFINITY : true, SQRT1_2 : true, SQRT2 : true }, strict_mode, syntax = {}, tab, token, urls, var_mode, warnings, // widget contains the global names which are provided to a Yahoo // (fna Konfabulator) widget. widget = { alert : true, animator : true, appleScript : true, beep : true, bytesToUIString : true, Canvas : true, chooseColor : true, chooseFile : true, chooseFolder : true, closeWidget : true, COM : true, convertPathToHFS : true, convertPathToPlatform : true, CustomAnimation : true, escape : true, FadeAnimation : true, filesystem : true, Flash : true, focusWidget : true, form : true, FormField : true, Frame : true, HotKey : true, Image : true, include : true, isApplicationRunning : true, iTunes : true, konfabulatorVersion : true, log : true, md5 : true, MenuItem : true, MoveAnimation : true, openURL : true, play : true, Point : true, popupMenu : true, preferenceGroups : true, preferences : true, print : true, prompt : true, random : true, Rectangle : true, reloadWidget : true, ResizeAnimation : true, resolvePath : true, resumeUpdates : true, RotateAnimation : true, runCommand : true, runCommandInBg : true, saveAs : true, savePreferences : true, screen : true, ScrollBar : true, showWidgetPreferences : true, sleep : true, speak : true, Style : true, suppressUpdates : true, system : true, tellWidget : true, Text : true, TextArea : true, Timer : true, unescape : true, updateNow : true, URL : true, Web : true, widget : true, Window : true, XMLDOM : true, XMLHttpRequest : true, yahooCheckLogin : true, yahooLogin : true, yahooLogout : true }, windows = { ActiveXObject: false, CScript : false, Debug : false, Enumerator : false, System : false, VBArray : false, WScript : false }, // xmode is used to adapt to the exceptions in html parsing. // It can have these states: // false .js script file // html // outer // script // style // scriptstring // styleproperty xmode, xquote, // Regular expressions. Some of these are stupidly long. // unsafe comment or string ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i, // unsafe characters that are silently deleted by one or more browsers cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, // token tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jslint|properties|members|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, // html token hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/, // characters in strings that need escapement nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, // outer html token ox = /[>&]|<[\/!]?|--/, // star slash lx = /\*\/|\/\*/, // identifier ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, // javascript url jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, // url badness ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i, // style sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, // attributes characters qx = /[^a-zA-Z0-9+\-_\/ ]/, // query characters for ids dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, rx = { outer: hx, html: hx, style: sx, styleproperty: ssx }; function return_this() { return this; } function F() {} // Used by Object.create // Provide critical ES5 functions to ES3. if (typeof Array.prototype.filter !== 'function') { Array.prototype.filter = function (f) { var i, length = this.length, result = []; for (i = 0; i < length; i += 1) { try { result.push(f(this[i])); } catch (ignore) { } } return result; }; } if (typeof Array.isArray !== 'function') { Array.isArray = function (o) { return Object.prototype.toString.apply(o) === '[object Array]'; }; } if (!Object.hasOwnProperty('create')) { Object.create = function (o) { F.prototype = o; return new F(); }; } if (typeof Object.keys !== 'function') { Object.keys = function (o) { var array = [], key; for (key in o) { if (Object.prototype.hasOwnProperty.call(o, key)) { array.push(key); } } return array; }; } // Substandard methods if (typeof String.prototype.entityify !== 'function') { String.prototype.entityify = function () { return this .replace(/&/g, '&') .replace(//g, '>'); }; } if (typeof String.prototype.isAlpha !== 'function') { String.prototype.isAlpha = function () { return (this >= 'a' && this <= 'z\uffff') || (this >= 'A' && this <= 'Z\uffff'); }; } if (typeof String.prototype.isDigit !== 'function') { String.prototype.isDigit = function () { return (this >= '0' && this <= '9'); }; } if (typeof String.prototype.supplant !== 'function') { String.prototype.supplant = function (o) { return this.replace(/\{([^{}]*)\}/g, function (a, b) { var replacement = o[b]; return typeof replacement === 'string' || typeof replacement === 'number' ? replacement : a; }); }; } if (typeof String.prototype.name !== 'function') { String.prototype.name = function () { // If the string looks like an identifier, then we can return it as is. // If the string contains no control characters, no quote characters, and no // backslash characters, then we can simply slap some quotes around it. // Otherwise we must also replace the offending characters with safe // sequences. if (ix.test(this)) { return this; } if (nx.test(this)) { return '"' + this.replace(nxg, function (a) { if (escapes[a]) { return escapes[a]; } return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); }) + '"'; } return '"' + this + '"'; }; } function combine(a, b) { var name; for (name in b) { if (Object.prototype.hasOwnProperty.call(b, name)) { a[name] = b[name]; } } } function assume() { if (!option.safe) { if (option.rhino) { combine(predefined, rhino); } if (option.devel) { combine(predefined, devel); } if (option.browser) { combine(predefined, browser); } if (option.windows) { combine(predefined, windows); } if (option.node) { combine(predefined, node); } if (option.widget) { combine(predefined, widget); } } } // Produce an error warning. function quit(message, line, character) { throw { name: 'JSLintError', line: line, character: character, message: bundle.scanned_a_b.supplant({ a: message, b: Math.floor((line / lines.length) * 100) }) }; } function warn(message, offender, a, b, c, d) { var character, line, warning; offender = offender || next_token; // `~ line = offender.line || 0; character = offender.from || 0; warning = { id: '(error)', raw: bundle[message] || message, evidence: lines[line - 1] || '', line: line, character: character, a: a || offender.value, b: b, c: c, d: d }; warning.reason = warning.raw.supplant(warning); JSLINT.errors.push(warning); if (option.passfail) { quit(bundle.stopping, line, character); } warnings += 1; if (warnings >= option.maxerr) { quit(bundle.too_many, line, character); } return warning; } function warn_at(message, line, character, a, b, c, d) { return warn(message, { line: line, from: character }, a, b, c, d); } function fail(message, offender, a, b, c, d) { var warning = warn(message, offender, a, b, c, d); quit(bundle.stopping, warning.line, warning.character); } function fail_at(message, line, character, a, b, c, d) { return fail(message, { line: line, from: character }, a, b, c, d); } function expected_at(at) { if (option.white && next_token.from !== at) { warn('expected_a_at_b_c', next_token, next_token.value, at, next_token.from); } } function aint(it, name, expected) { if (it[name] !== expected) { warn('expected_a_b', it, expected, it[name]); return true; } else { return false; } } // lexical analysis and token construction var lex = (function lex() { var character, from, line, source_row; // Private lex methods function collect_comment(comment, quote, line, at) { var comment_object = { comment: comment, quote: quote, at: at, line: line }; if (comments_off || src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) { warn_at('unexpected_comment', line, character); } else if (xmode === 'script' && /<\//i.test(source_row)) { warn_at('unexpected_a', line, character, '<\/'); } else if (option.safe && ax.test(comment)) { warn_at('dangerous_comment', line, at); } if (older_token.comments) { older_token.comments.push(comment_object); } else { older_token.comments = [comment_object]; } JSLINT.comments.push(comment_object); } function next_line() { var at; if (line >= lines.length) { return false; } character = 1; source_row = lines[line]; line += 1; at = source_row.search(/ \t/); if (at >= 0) { warn_at('mixed', line, at + 1); } source_row = source_row.replace(/\t/g, tab); at = source_row.search(cx); if (at >= 0) { warn_at('unsafe', line, at); } if (option.maxlen && option.maxlen < source_row.length) { warn_at('too_long', line, source_row.length); } return true; } // Produce a token object. The token inherits from a syntax symbol. function it(type, value, quote) { var id, the_token; if (type === '(string)' || type === '(range)') { if (jx.test(value)) { warn_at('url', line, from); } } the_token = Object.create(syntax[( type === '(punctuator)' || (type === '(identifier)' && Object.prototype.hasOwnProperty.call(syntax, value)) ? value : type )] || syntax['(error)']); if (type === '(identifier)') { the_token.identifier = true; if (value === '__iterator__' || value === '__proto__') { fail_at('reserved_a', line, from, value); } else if (option.nomen && (value.charAt(0) === '_' || value.charAt(value.length - 1) === '_')) { warn_at('dangling_a', line, from, value); } } if (value !== undefined) { the_token.value = value; } if (quote) { the_token.quote = quote; } the_token.line = line; the_token.from = from; the_token.thru = character; the_token.prev = older_token; id = the_token.id; prereg = id && ( ('(,=:[!&|?{};'.indexOf(id.charAt(id.length - 1)) >= 0) || id === 'return' ); older_token.next = the_token; older_token = the_token; return the_token; } // Public lex methods return { init: function (source) { if (typeof source === 'string') { lines = source .replace(/\r\n/g, '\n') .replace(/\r/g, '\n') .split('\n'); } else { lines = source; } line = 0; next_line(); from = 1; }, range: function (begin, end) { var c, value = ''; from = character; if (source_row.charAt(0) !== begin) { fail_at('expected_a_b', line, character, begin, source_row.charAt(0)); } for (;;) { source_row = source_row.slice(1); character += 1; c = source_row.charAt(0); switch (c) { case '': fail_at('missing_a', line, character, c); break; case end: source_row = source_row.slice(1); character += 1; return it('(range)', value); case xquote: case '\\': warn_at('unexpected_a', line, character, c); break; } value += c; } }, // token -- this is called by advance to get the next token. token: function () { var b, c, captures, digit, depth, flag, high, i, j, length, low, quote, symbol; function match(x) { var exec = x.exec(source_row), first; if (exec) { length = exec[0].length; first = exec[1]; c = first.charAt(0); source_row = source_row.substr(length); from = character + length - first.length; character += length; return first; } } function string(x) { var c, j, r = ''; function hex(n) { var i = parseInt(source_row.substr(j + 1, n), 16); j += n; if (i >= 32 && i <= 126 && i !== 34 && i !== 92 && i !== 39) { warn_at('unexpected_a', line, character, '\\'); } character += n; c = String.fromCharCode(i); } if (json_mode && x !== '"') { warn_at('expected_a', line, character, '"'); } if (xquote === x || (xmode === 'scriptstring' && !xquote)) { return it('(punctuator)', x); } j = 0; for (;;) { while (j >= source_row.length) { j = 0; if (xmode !== 'html' || !next_line()) { fail_at('unclosed', line, from); } } c = source_row.charAt(j); if (c === x) { character += 1; source_row = source_row.substr(j + 1); return it('(string)', r, x); } if (c < ' ') { if (c === '\n' || c === '\r') { break; } warn_at('control_a', line, character + j, source_row.slice(0, j)); } else if (c === xquote) { warn_at('bad_html', line, character + j); } else if (c === '<') { if (option.safe && xmode === 'html') { warn_at('adsafe_a', line, character + j, c); } else if (source_row.charAt(j + 1) === '/' && (xmode || option.safe)) { warn_at('expected_a_b', line, character, '<\\/', ' 0) { character += 1; source_row = source_row.slice(i); break; } else { if (!next_line()) { return it('(end)', ''); } } } symbol = match(rx[xmode] || tx); if (!symbol) { symbol = ''; c = ''; while (source_row && source_row < '!') { source_row = source_row.substr(1); } if (source_row) { if (xmode === 'html') { return it('(error)', source_row.charAt(0)); } else { fail_at('unexpected_a', line, character, source_row.substr(0, 1)); } } } else { // identifier if (c.isAlpha() || c === '_' || c === '$') { return it('(identifier)', symbol); } // number if (c.isDigit()) { if (xmode !== 'style' && xmode !== 'styleproperty' && source_row.substr(0, 1).isAlpha()) { warn_at('expected_space_a_b', line, character, c, source_row.charAt(0)); } if (c === '0') { digit = symbol.substr(1, 1); if (digit.isDigit()) { if (token.id !== '.' && xmode !== 'styleproperty') { warn_at('unexpected_a', line, character, symbol); } } else if (json_mode && (digit === 'x' || digit === 'X')) { warn_at('unexpected_a', line, character, '0x'); } } if (symbol.substr(symbol.length - 1) === '.') { warn_at('trailing_decimal_a', line, character, symbol); } if (xmode !== 'style') { digit = +symbol; if (!isFinite(digit)) { warn_at('bad_number', line, character, symbol); } symbol = digit; } return it('(number)', symbol); } switch (symbol) { // string case '"': case "'": return string(symbol); // // comment case '//': collect_comment(source_row, '//', line, character); source_row = ''; break; // /* comment case '/*': quote = '/*'; for (;;) { i = source_row.search(lx); if (i >= 0) { break; } collect_comment(source_row, quote, line, character); quote = ''; if (!next_line()) { fail_at('unclosed_comment', line, character); } } collect_comment(source_row.slice(0, i), quote, character, line); character += i + 2; if (source_row.substr(i, 1) === '/') { fail_at('nested_comment', line, character); } source_row = source_row.substr(i + 2); break; case '': break; // / case '/': if (token.id === '/=') { fail_at( bundle.slash_equal, line, from ); } if (prereg) { depth = 0; captures = 0; length = 0; for (;;) { b = true; c = source_row.charAt(length); length += 1; switch (c) { case '': fail_at('unclosed_regexp', line, from); return; case '/': if (depth > 0) { warn_at('unescaped_a', line, from + length, '/'); } c = source_row.substr(0, length - 1); flag = Object.create(regexp_flag); while (flag[source_row.charAt(length)] === true) { flag[source_row.charAt(length)] = false; length += 1; } if (source_row.charAt(length).isAlpha()) { fail_at('unexpected_a', line, from, source_row.charAt(length)); } character += length; source_row = source_row.substr(length); quote = source_row.charAt(0); if (quote === '/' || quote === '*') { fail_at('confusing_regexp', line, from); } return it('(regexp)', c); case '\\': c = source_row.charAt(length); if (c < ' ') { warn_at('control_a', line, from + length, String(c)); } else if (c === '<') { warn_at( bundle.unexpected_a, line, from + length, '\\' ); } length += 1; break; case '(': depth += 1; b = false; if (source_row.charAt(length) === '?') { length += 1; switch (source_row.charAt(length)) { case ':': case '=': case '!': length += 1; break; default: warn_at( bundle.expected_a_b, line, from + length, ':', source_row.charAt(length) ); } } else { captures += 1; } break; case '|': b = false; break; case ')': if (depth === 0) { warn_at('unescaped_a', line, from + length, ')'); } else { depth -= 1; } break; case ' ': j = 1; while (source_row.charAt(length) === ' ') { length += 1; j += 1; } if (j > 1) { warn_at('use_braces', line, from + length, j); } break; case '[': c = source_row.charAt(length); if (c === '^') { length += 1; if (option.regexp) { warn_at('insecure_a', line, from + length, c); } else if (source_row.charAt(length) === ']') { fail_at('unescaped_a', line, from + length, '^'); } } quote = false; if (c === ']') { warn_at('empty_class', line, from + length - 1); quote = true; } klass: do { c = source_row.charAt(length); length += 1; switch (c) { case '[': case '^': warn_at('unescaped_a', line, from + length, c); quote = true; break; case '-': if (quote) { quote = false; } else { warn_at('unescaped_a', line, from + length, '-'); quote = true; } break; case ']': if (!quote) { warn_at('unescaped_a', line, from + length - 1, '-'); } break klass; case '\\': c = source_row.charAt(length); if (c < ' ') { warn_at( bundle.control_a, line, from + length, String(c) ); } else if (c === '<') { warn_at( bundle.unexpected_a, line, from + length, '\\' ); } length += 1; quote = true; break; case '/': warn_at('unescaped_a', line, from + length - 1, '/'); quote = true; break; case '<': if (xmode === 'script') { c = source_row.charAt(length); if (c === '!' || c === '/') { warn_at( bundle.html_confusion_a, line, from + length, c ); } } quote = true; break; default: quote = true; } } while (c); break; case '.': if (option.regexp) { warn_at('insecure_a', line, from + length, c); } break; case ']': case '?': case '{': case '}': case '+': case '*': warn_at('unescaped_a', line, from + length, c); break; case '<': if (xmode === 'script') { c = source_row.charAt(length); if (c === '!' || c === '/') { warn_at( bundle.html_confusion_a, line, from + length, c ); } } break; } if (b) { switch (source_row.charAt(length)) { case '?': case '+': case '*': length += 1; if (source_row.charAt(length) === '?') { length += 1; } break; case '{': length += 1; c = source_row.charAt(length); if (c < '0' || c > '9') { warn_at( bundle.expected_number_a, line, from + length, c ); } length += 1; low = +c; for (;;) { c = source_row.charAt(length); if (c < '0' || c > '9') { break; } length += 1; low = +c + (low * 10); } high = low; if (c === ',') { length += 1; high = Infinity; c = source_row.charAt(length); if (c >= '0' && c <= '9') { length += 1; high = +c; for (;;) { c = source_row.charAt(length); if (c < '0' || c > '9') { break; } length += 1; high = +c + (high * 10); } } } if (source_row.charAt(length) !== '}') { warn_at( bundle.expected_a_b, line, from + length, '}', c ); } else { length += 1; } if (source_row.charAt(length) === '?') { length += 1; } if (low > high) { warn_at( bundle.not_greater, line, from + length, low, high ); } break; } } } c = source_row.substr(0, length - 1); character += length; source_row = source_row.substr(length); return it('(regexp)', c); } return it('(punctuator)', symbol); // punctuator case ''); } character += 3; source_row = source_row.slice(i + 3); break; case '#': if (xmode === 'html' || xmode === 'styleproperty') { for (;;) { c = source_row.charAt(0); if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F')) { break; } character += 1; source_row = source_row.substr(1); symbol += c; } if (symbol.length !== 4 && symbol.length !== 7) { warn_at('bad_color_a', line, from + length, symbol); } return it('(color)', symbol); } return it('(punctuator)', symbol); default: if (xmode === 'outer' && c === '&') { character += 1; source_row = source_row.substr(1); for (;;) { c = source_row.charAt(0); character += 1; source_row = source_row.substr(1); if (c === ';') { break; } if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || c === '#')) { fail_at('bad_entity', line, from + length, character); } } break; } return it('(punctuator)', symbol); } } } } }; }()); function add_label(symbol, type) { if (option.safe && funct['(global)'] && typeof predefined[symbol] !== 'boolean') { warn('adsafe_a', token, symbol); } else if (symbol === 'hasOwnProperty') { warn('bad_name_a', token, symbol); } // Define symbol in the current function in the current scope. if (Object.prototype.hasOwnProperty.call(funct, symbol) && !funct['(global)']) { warn(funct[symbol] === true ? bundle.used_before_a : bundle.already_defined, next_token, symbol); } funct[symbol] = type; if (funct['(global)']) { if (global[symbol] === false) { warn('read_only'); } global[symbol] = true; if (Object.prototype.hasOwnProperty.call(implied, symbol)) { warn('used_before_a', next_token, symbol); delete implied[symbol]; } } else { scope[symbol] = funct; } } function peek(distance) { // Peek ahead to a future token. The distance is how far ahead to look. The // default is the next token. var found, slot = 0; distance = distance || 0; while (slot <= distance) { found = lookahead[slot]; if (!found) { found = lookahead[slot] = lex.token(); } slot += 1; } return found; } function discard(it) { // The token will not be included in the parse tree, so move the comments // that are attached to the token to tokens that are in the tree. it = it || token; if (it.comments) { var prev = it.prev; while (prev.comments === null) { prev = prev.prev; } if (prev.comments) { prev.comments = prev.comments.concat(it.comments); } else { prev.comments = it.comments; } } it.comments = null; } function advance(id, match) { // Produce the next token, also looking for programming errors. if (indent) { // In indentation checking was requested, then inspect all of the line breakings. // The var statement is tricky because the names might be aligned or not. We // look at the first line break after the var to determine the programmer's // intention. if (var_mode && next_token.line !== token.line) { if ((var_mode !== indent || !next_token.edge) && next_token.from === indent.at - (next_token.edge ? option.indent : 0)) { var dent = indent; for (;;) { dent.at -= option.indent; if (dent === var_mode) { break; } dent = dent.was; } dent.open = false; } var_mode = false; } if (indent.open) { // If the token is an edge. if (next_token.edge) { if (next_token.edge === 'label') { expected_at(1); } else if (next_token.edge === 'case') { expected_at(indent.at - option.indent); } else if (indent.mode !== 'array' || next_token.line !== token.line) { expected_at(indent.at); } // If the token is not an edge, but is the first token on the line. } else if (next_token.line !== token.line && next_token.from < indent.at + (indent.mode === 'expression' ? 0 : option.indent)) { expected_at(indent.at + option.indent); } } else if (next_token.line !== token.line) { if (next_token.edge) { expected_at(indent.at); } else { indent.wrap = true; if (indent.mode === 'statement' || indent.mode === 'var') { expected_at(indent.at + option.indent); } else if (next_token.from < indent.at + (indent.mode === 'expression' ? 0 : option.indent)) { expected_at(indent.at + option.indent); } } } } switch (token.id) { case '(number)': if (next_token.id === '.') { warn('trailing_decimal_a'); } break; case '-': if (next_token.id === '-' || next_token.id === '--') { warn('confusing_a'); } break; case '+': if (next_token.id === '+' || next_token.id === '++') { warn('confusing_a'); } break; } if (token.arity === 'string' || token.identifier) { anonname = token.value; } if (id && next_token.id !== id) { if (match) { warn('expected_a_b_from_c_d', next_token, id, match.id, match.line, next_token.value); } else if (!next_token.identifier || next_token.value !== id) { warn('expected_a_b', next_token, id, next_token.value); } } prev_token = token; token = next_token; next_token = lookahead.shift() || lex.token(); if (token.id === '(end)') { discard(); } } function directive() { var command = this.id, name, old_comments_off = comments_off, old_option_white = option.white, value; comments_off = true; option.white = false; if (lookahead.length > 0 || next_token.comments) { warn('unexpected_a', this); } switch (command) { case '/*properties': case '/*members': command = '/*properties'; if (!properties) { properties = {}; } break; case '/*jslint': if (option.safe) { warn('adsafe_a', this); } break; case '/*global': if (option.safe) { warn('adsafe_a', this); } break; default: fail('unpexpected_a', this); } loop: for (;;) { for (;;) { if (next_token.id === '*/') { break loop; } if (next_token.id !== ',') { break; } advance(); } if (next_token.arity !== 'string' && !next_token.identifier) { fail('unexpected_a', next_token); } name = next_token.value; advance(); switch (command) { case '/*global': if (next_token.id === ':') { advance(':'); switch (next_token.id) { case 'true': if (typeof scope[name] === 'object' || global[name] === false) { fail('unexpected_a'); } global[name] = true; advance('true'); break; case 'false': if (typeof scope[name] === 'object') { fail('unexpected_a'); } global[name] = false; advance('false'); break; default: fail('unexpected_a'); } } else { if (typeof scope[name] === 'object') { fail('unexpected_a'); } global[name] = false; } break; case '/*jslint': if (next_token.id !== ':') { fail('expected_a_b', next_token, ':', next_token.value); } advance(':'); switch (name) { case 'indent': value = +next_token.value; if (typeof value !== 'number' || !isFinite(value) || value < 0 || Math.floor(value) !== value) { fail('expected_small_a'); } if (value > 0) { old_option_white = true; } option.indent = value; break; case 'maxerr': value = +next_token.value; if (typeof value !== 'number' || !isFinite(value) || value <= 0 || Math.floor(value) !== value) { fail('expected_small_a', next_token); } option.maxerr = value; break; case 'maxlen': value = +next_token.value; if (typeof value !== 'number' || !isFinite(value) || value < 0 || Math.floor(value) !== value) { fail('expected_small_a'); } option.maxlen = value; break; case 'white': if (next_token.id === 'true') { old_option_white = true; } else if (next_token.id === 'false') { old_option_white = false; } else { fail('unexpected_a'); } break; default: if (next_token.id === 'true') { option[name] = true; } else if (next_token.id === 'false') { option[name] = false; } else { fail('unexpected_a'); } } advance(); break; case '/*properties': properties[name] = true; break; default: fail('unexpected_a'); } } if (command === '/*jslint') { assume(); } comments_off = old_comments_off; advance('*/'); option.white = old_option_white; } // Indentation intention function edge(mode) { next_token.edge = !indent || (indent.open && (mode || true)); } function step_in(mode) { var open, was; if (typeof mode === 'number') { indent = { at: mode, open: true, was: was }; } else if (!indent) { indent = { at: 1, mode: 'statement', open: true }; } else { was = indent; open = mode === 'var' || (next_token.line !== token.line && mode !== 'statement'); indent = { at: (open || mode === 'control' ? was.at + option.indent : was.at) + (was.wrap ? option.indent : 0), mode: mode, open: open, was: was }; if (mode === 'var' && open) { var_mode = indent; } } } function step_out(id, symbol) { if (id) { if (indent && indent.open) { indent.at -= option.indent; edge(); } advance(id, symbol); } if (indent) { indent = indent.was; } } // Functions for conformance of whitespace. function one_space(left, right) { left = left || token; right = right || next_token; if (right.id !== '(end)' && option.white && (token.line !== right.line || token.thru + 1 !== right.from)) { warn('expected_space_a_b', right, token.value, right.value); } } function one_space_only(left, right) { left = left || token; right = right || next_token; if (right.id !== '(end)' && (left.line !== right.line || (option.white && left.thru + 1 !== right.from))) { warn('expected_space_a_b', right, left.value, right.value); } } function no_space(left, right) { left = left || token; right = right || next_token; if ((option.white || xmode === 'styleproperty' || xmode === 'style') && left.thru !== right.from && left.line === right.line) { warn('unexpected_space_a_b', right, left.value, right.value); } } function no_space_only(left, right) { left = left || token; right = right || next_token; if (right.id !== '(end)' && (left.line !== right.line || (option.white && left.thru !== right.from))) { warn('unexpected_space_a_b', right, left.value, right.value); } } function spaces(left, right) { if (option.white) { left = left || token; right = right || next_token; if (left.thru === right.from && left.line === right.line) { warn('missing_space_a_b', right, left.value, right.value); } } } function comma() { if (next_token.id !== ',') { warn_at('expected_a_b', token.line, token.thru, ',', next_token.value); } else { if (option.white) { no_space_only(); } advance(','); discard(); spaces(); } } function semicolon() { if (next_token.id !== ';') { warn_at('expected_a_b', token.line, token.thru, ';', next_token.value); } else { if (option.white) { no_space_only(); } advance(';'); discard(); if (semicolon_coda[next_token.id] !== true) { spaces(); } } } function use_strict() { if (next_token.value === 'use strict') { if (strict_mode) { warn('unnecessary_use'); } edge(); advance(); semicolon(); strict_mode = true; option.newcap = true; option.undef = true; return true; } else { return false; } } function are_similar(a, b) { if (a === b) { return true; } if (Array.isArray(a)) { if (Array.isArray(b) && a.length === b.length) { var i; for (i = 0; i < a.length; i += 1) { if (!are_similar(a[i], b[i])) { return false; } } return true; } return false; } if (Array.isArray(b)) { return false; } if (a.arity === b.arity && a.value === b.value) { switch (a.arity) { case 'prefix': case 'suffix': case undefined: return are_similar(a.first, b.first); case 'infix': return are_similar(a.first, b.first) && are_similar(a.second, b.second); case 'ternary': return are_similar(a.first, b.first) && are_similar(a.second, b.second) && are_similar(a.third, b.third); case 'function': case 'regexp': return false; default: return true; } } else { if (a.id === '.' && b.id === '[' && b.arity === 'infix') { return a.second.value === b.second.value && b.second.arity === 'string'; } else if (a.id === '[' && a.arity === 'infix' && b.id === '.') { return a.second.value === b.second.value && a.second.arity === 'string'; } } return false; } // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is // like .nud except that it is only used on the first token of a statement. // Having .fud makes it much easier to define statement-oriented languages like // JavaScript. I retained Pratt's nomenclature. // .nud Null denotation // .fud First null denotation // .led Left denotation // lbp Left binding power // rbp Right binding power // They are elements of the parsing method called Top Down Operator Precedence. function expression(rbp, initial) { // rbp is the right binding power. // initial indicates that this is the first expression of a statement. var left; if (next_token.id === '(end)') { fail('unexpected_a', token, next_token.id); } advance(); if (option.safe && typeof predefined[token.value] === 'boolean' && (next_token.id !== '(' && next_token.id !== '.')) { warn('adsafe', token); } if (initial) { anonname = 'anonymous'; funct['(verb)'] = token.value; } if (initial === true && token.fud) { left = token.fud(); } else { if (token.nud) { left = token.nud(); } else { if (next_token.arity === 'number' && token.id === '.') { warn('leading_decimal_a', token, next_token.value); advance(); return token; } else { fail('expected_identifier_a', token, token.id); } } while (rbp < next_token.lbp) { advance(); if (token.led) { left = token.led(left); } else { fail('expected_operator_a', token, token.id); } } } return left; } // Functional constructors for making the symbols that will be inherited by // tokens. function symbol(s, p) { var x = syntax[s]; if (!x || typeof x !== 'object') { syntax[s] = x = { id: s, lbp: p, value: s }; } return x; } function delim(s) { return symbol(s, 0); } function postscript(x) { x.postscript = true; return x; } function ultimate(s) { var x = symbol(s, 0); x.from = 1; x.thru = 1; x.line = 0; x.edge = true; s.value = s; return postscript(x); } function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; x.fud = f; return x; } function labeled_stmt(s, f) { var x = stmt(s, f); x.labeled = true; } function disrupt_stmt(s, f) { var x = stmt(s, f); x.disrupt = true; } function reserve_name(x) { var c = x.id.charAt(0); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { x.identifier = x.reserved = true; } return x; } function prefix(s, f) { var x = symbol(s, 150); reserve_name(x); x.nud = (typeof f === 'function') ? f : function () { if (s === 'typeof') { one_space(); } else { no_space_only(); } this.first = expression(150); this.arity = 'prefix'; if (this.id === '++' || this.id === '--') { if (option.plusplus) { warn('unexpected_a', this); } else if ((!this.first.identifier || this.first.reserved) && this.first.id !== '.' && this.first.id !== '[') { warn('bad_operand', this); } } return this; }; return x; } function type(s, arity, nud) { var x = delim(s); x.arity = arity; if (nud) { x.nud = nud; } return x; } function reserve(s, f) { var x = delim(s); x.identifier = x.reserved = true; if (typeof f === 'function') { x.nud = f; } return x; } function reservevar(s, v) { return reserve(s, function () { if (typeof v === 'function') { v(this); } return this; }); } function infix(s, p, f, w) { var x = symbol(s, p); reserve_name(x); x.led = function (left) { this.arity = 'infix'; if (!w) { spaces(prev_token, token); spaces(); } if (typeof f === 'function') { return f(left, this); } else { this.first = left; this.second = expression(p); return this; } }; return x; } function expected_relation(node, message) { if (node.assign) { warn(message || bundle.conditional_assignment, node); } return node; } function expected_condition(node, message) { switch (node.id) { case '[': case '-': if (node.arity !== 'infix') { warn(message || bundle.weird_condition, node); } break; case 'false': case 'function': case 'Infinity': case 'NaN': case 'null': case 'true': case 'undefined': case 'void': case '(number)': case '(regexp)': case '(string)': case '{': warn(message || bundle.weird_condition, node); break; } return node; } function check_relation(node) { switch (node.arity) { case 'prefix': switch (node.id) { case '{': case '[': warn('unexpected_a', node); break; case '!': warn('confusing_a', node); break; } break; case 'function': case 'regexp': warn('unexpected_a', node); break; default: if (node.id === 'NaN') { warn('isnan', node); } } return node; } function relation(s, eqeq) { var x = infix(s, 100, function (left, that) { check_relation(left); if (eqeq) { warn('expected_a_b', that, eqeq, that.id); } var right = expression(100); if (are_similar(left, right) || ((left.arity === 'string' || left.arity === 'number') && (right.arity === 'string' || right.arity === 'number'))) { warn('weird_relation', that); } that.first = left; that.second = check_relation(right); return that; }); return x; } function assignop(s, bit) { var x = infix(s, 20, function (left, that) { var l; if (option.bitwise && bit) { warn('unexpected_a', that); } that.first = left; if (funct[left.value] === false) { warn('read_only', left); } else if (left['function']) { warn('a_function', left); } if (option.safe) { l = left; do { if (typeof predefined[l.value] === 'boolean') { warn('adsafe', l); } l = l.first; } while (l); } if (left) { if (left === syntax['function']) { warn('identifier_function', token); } if (left.id === '.' || left.id === '[') { if (!left.first || left.first.value === 'arguments') { warn('bad_assignment', that); } that.second = expression(19); if (that.id === '=' && are_similar(that.first, that.second)) { warn('weird_assignment', that); } return that; } else if (left.identifier && !left.reserved) { if (funct[left.value] === 'exception') { warn('assign_exception', left); } that.second = expression(19); if (that.id === '=' && are_similar(that.first, that.second)) { warn('weird_assignment', that); } return that; } } fail('bad_assignment', that); }); x.assign = true; return x; } function bitwise(s, p) { return infix(s, p, function (left, that) { if (option.bitwise) { warn('unexpected_a', that); } that.first = left; that.second = expression(p); return that; }); } function suffix(s, f) { var x = symbol(s, 150); x.led = function (left) { no_space_only(prev_token, token); if (option.plusplus) { warn('unexpected_a', this); } else if ((!left.identifier || left.reserved) && left.id !== '.' && left.id !== '[') { warn('bad_operand', this); } this.first = left; this.arity = 'suffix'; return this; }; return x; } function optional_identifier() { if (next_token.identifier) { advance(); if (option.safe && banned[token.value]) { warn('adsafe_a', token); } else if (token.reserved && !option.es5) { warn('expected_identifier_a_reserved', token); } return token.value; } } function identifier() { var i = optional_identifier(); if (i) { return i; } if (token.id === 'function' && next_token.id === '(') { warn('name_function'); } else { fail('expected_identifier_a'); } } function statement(no_indent) { // Usually a statement starts a line. Exceptions include the var statement in the // initialization part of a for statement, and an if after an else. var label, old_scope = scope, the_statement; // We don't like the empty statement. if (next_token.id === ';') { warn('unexpected_a'); semicolon(); return; } // Is this a labeled statement? if (next_token.identifier && !next_token.reserved && peek().id === ':') { edge('label'); label = next_token; advance(); discard(); advance(':'); discard(); scope = Object.create(old_scope); add_label(label.value, 'label'); if (next_token.labeled !== true) { warn('label_a_b', next_token, label.value, next_token.value); } if (jx.test(label.value + ':')) { warn('url', label); } next_token.label = label; } // Parse the statement. edge(); step_in('statement'); the_statement = expression(0, true); if (the_statement) { // Look for the final semicolon. if (the_statement.arity === 'statement') { if (the_statement.id === 'switch' || (the_statement.block && the_statement.id !== 'do')) { spaces(); } else { semicolon(); } } else { // If this is an expression statement, determine if it is acceptble. // We do not like // new Blah(); // statments. If it is to be used at all, new should only be used to make // objects, not side effects. The expression statements we do like do // assignment or invocation or delete. if (the_statement.id === '(') { if (the_statement.first.id === 'new') { warn('bad_new'); } } else if (!the_statement.assign && the_statement.id !== 'delete' && the_statement.id !== '++' && the_statement.id !== '--') { warn('assignment_function_expression', token); } semicolon(); } } step_out(); scope = old_scope; return the_statement; } function statements() { var array = [], disruptor, the_statement; // A disrupt statement may not be followed by any other statement. // If the last statement is disrupt, then the sequence is disrupt. while (next_token.postscript !== true) { if (next_token.id === ';') { warn('unexpected_a', next_token); semicolon(); } else { if (disruptor) { warn('unreachable_a_b', next_token, next_token.value, disruptor.value); disruptor = null; } the_statement = statement(); if (the_statement) { array.push(the_statement); if (the_statement.disrupt) { disruptor = the_statement; array.disrupt = true; } } } } return array; } function block(ordinary) { // array block is array sequence of statements wrapped in braces. // ordinary is false for function bodies and try blocks. // ordinary is true for if statements, while, etc. var array, curly = next_token, old_inblock = in_block, old_scope = scope, old_strict_mode = strict_mode; in_block = ordinary; scope = Object.create(scope); spaces(); if (next_token.id === '{') { advance('{'); step_in(); if (!ordinary && !use_strict() && !old_strict_mode && option.strict && funct['(context)']['(global)']) { warn('missing_use_strict'); } array = statements(); strict_mode = old_strict_mode; step_out('}', curly); discard(); } else if (!ordinary) { fail('expected_a_b', next_token, '{', next_token.value); } else { warn('expected_a_b', next_token, '{', next_token.value); array = [statement()]; array.disrupt = array[0].disrupt; } funct['(verb)'] = null; scope = old_scope; in_block = old_inblock; if (ordinary && array.length === 0) { warn('empty_block'); } return array; } function tally_property(name) { if (properties && typeof properties[name] !== 'boolean') { warn('unexpected_member_a', token, name); } if (typeof member[name] === 'number') { member[name] += 1; } else { member[name] = 1; } } function note_implied(token) { var name = token.value, line = token.line, a = implied[name]; if (typeof a === 'function') { a = false; } if (!a) { a = [line]; implied[name] = a; } else if (a[a.length - 1] !== line) { a.push(line); } } // ECMAScript parser syntax['(identifier)'] = { type: '(identifier)', lbp: 0, identifier: true, nud: function () { var variable = this.value, site = scope[variable]; if (typeof site === 'function') { site = undefined; } // The name is in scope and defined in the current function. if (funct === site) { // Change 'unused' to 'var', and reject labels. switch (funct[variable]) { case 'error': warn('unexpected_a', token); funct[variable] = 'var'; break; case 'unused': funct[variable] = 'var'; break; case 'unction': funct[variable] = 'function'; this['function'] = true; break; case 'function': this['function'] = true; break; case 'label': warn('a_label', token, variable); break; } // The name is not defined in the function. If we are in the global scope, // then we have an undefined variable. } else if (funct['(global)']) { if (typeof global[variable] === 'boolean') { funct[variable] = global[variable]; } else { if (option.undef) { warn('not_a_defined', token, variable); } else { note_implied(token); } } // If the name is already defined in the current // function, but not as outer, then there is a scope error. } else { switch (funct[variable]) { case 'closure': case 'function': case 'var': case 'unused': warn('a_scope', token, variable); break; case 'label': warn('a_label', token, variable); break; case 'outer': case true: case false: break; default: // If the name is defined in an outer function, make an outer entry, and if // it was unused, make it var. if (typeof site === 'boolean') { funct[variable] = site; functions[0][variable] = true; } else if (site === null) { warn('a_not_allowed', token, variable); note_implied(token); } else if (typeof site !== 'object') { if (option.undef) { warn('a_not_defined', token, variable); } else { funct[variable] = true; } note_implied(token); } else { switch (site[variable]) { case 'function': case 'unction': this['function'] = true; site[variable] = 'closure'; funct[variable] = site['(global)'] ? false : 'outer'; break; case 'var': case 'unused': site[variable] = 'closure'; funct[variable] = site['(global)'] ? true : 'outer'; break; case 'closure': case 'parameter': funct[variable] = site['(global)'] ? true : 'outer'; break; case 'error': warn('not_a_defined', token); break; case 'label': warn('a_label', token, variable); break; } } } } return this; }, led: function () { fail('expected_operator_a'); } }; // Build the syntax table by declaring the syntactic elements. type('(color)', 'color'); type('(number)', 'number', return_this); type('(string)', 'string', return_this); type('(range)', 'range'); type('(regexp)', 'regexp', return_this); ultimate('(begin)'); ultimate('(end)'); ultimate('(error)'); postscript(delim(''); postscript(delim('}')); delim(')'); delim(']'); postscript(delim('"')); postscript(delim('\'')); delim(';'); delim(':'); delim(','); delim('#'); delim('@'); delim('*/'); postscript(reserve('case')); reserve('catch'); postscript(reserve('default')); reserve('else'); reserve('finally'); reservevar('arguments', function (x) { if (strict_mode && funct['(global)']) { warn('strict', x); } else if (option.safe) { warn('adsafe', x); } }); reservevar('eval', function (x) { if (option.safe) { warn('adsafe', x); } }); reservevar('false'); reservevar('Infinity'); reservevar('NaN'); reservevar('null'); reservevar('this', function (x) { if (strict_mode && ((funct['(statement)'] && funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) { warn('strict', x); } else if (option.safe) { warn('adsafe', x); } }); reservevar('true'); reservevar('undefined'); assignop('='); assignop('+='); assignop('-='); assignop('*='); assignop('/=').nud = function () { fail('slash_equal'); }; assignop('%='); assignop('&=', true); assignop('|=', true); assignop('^=', true); assignop('<<=', true); assignop('>>=', true); assignop('>>>=', true); infix('?', 30, function (left, that) { that.first = expected_condition(expected_relation(left)); that.second = expression(0); spaces(); advance(':'); discard(); spaces(); that.third = expression(10); that.arity = 'ternary'; if (are_similar(that.second, that.third)) { warn('weird_ternary', that); } return that; }); infix('||', 40, function (left, that) { function paren_check(that) { if (that.id === '&&' && !that.paren) { warn('and', that); } return that; } that.first = paren_check(expected_condition(expected_relation(left))); that.second = paren_check(expected_relation(expression(40))); if (are_similar(that.first, that.second)) { warn('weird_condition', that); } return that; }); infix('&&', 50, function (left, that) { that.first = expected_condition(expected_relation(left)); that.second = expected_relation(expression(50)); if (are_similar(that.first, that.second)) { warn('weird_condition', that); } return that; }); prefix('void', function () { this.first = expression(0); if (this.first.arity !== 'number' || this.first.value) { warn('unexpected_a', this); return this; } return this; }); bitwise('|', 70); bitwise('^', 80); bitwise('&', 90); relation('==', '==='); relation('==='); relation('!=', '!=='); relation('!=='); relation('<'); relation('>'); relation('<='); relation('>='); bitwise('<<', 120); bitwise('>>', 120); bitwise('>>>', 120); infix('in', 120, function (left, that) { warn('infix_in', that); that.left = left; that.right = expression(130); return that; }); infix('instanceof', 120); infix('+', 130, function (left, that) { if (!left.value) { if (left.arity === 'number') { warn('unexpected_a', left); } else if (left.arity === 'string') { warn('expected_a_b', left, 'String', '\'\''); } } var right = expression(130); if (!right.value) { if (right.arity === 'number') { warn('unexpected_a', right); } else if (right.arity === 'string') { warn('expected_a_b', right, 'String', '\'\''); } } if (left.arity === right.arity && (left.arity === 'string' || left.arity === 'number')) { left.value += right.value; left.thru = right.thru; if (left.arity === 'string' && jx.test(left.value)) { warn('url', left); } discard(right); discard(that); return left; } that.first = left; that.second = right; return that; }); prefix('+', 'num'); prefix('+++', function () { warn('confusing_a', token); this.first = expression(150); this.arity = 'prefix'; return this; }); infix('+++', 130, function (left) { warn('confusing_a', token); this.first = left; this.second = expression(130); return this; }); infix('-', 130, function (left, that) { if ((left.arity === 'number' && left.value === 0) || left.arity === 'string') { warn('unexpected_a', left); } var right = expression(130); if ((right.arity === 'number' && right.value === 0) || right.arity === 'string') { warn('unexpected_a', left); } if (left.arity === right.arity && left.arity === 'number') { left.value -= right.value; left.thru = right.thru; discard(right); discard(that); return left; } that.first = left; that.second = right; return that; }); prefix('-'); prefix('---', function () { warn('confusing_a', token); this.first = expression(150); this.arity = 'prefix'; return this; }); infix('---', 130, function (left) { warn('confusing_a', token); this.first = left; this.second = expression(130); return this; }); infix('*', 140, function (left, that) { if ((left.arity === 'number' && (left.value === 0 || left.value === 1)) || left.arity === 'string') { warn('unexpected_a', left); } var right = expression(140); if ((right.arity === 'number' && (right.value === 0 || right.value === 1)) || right.arity === 'string') { warn('unexpected_a', right); } if (left.arity === right.arity && left.arity === 'number') { left.value *= right.value; left.thru = right.thru; discard(right); discard(that); return left; } that.first = left; that.second = right; return that; }); infix('/', 140, function (left, that) { if ((left.arity === 'number' && left.value === 0) || left.arity === 'string') { warn('unexpected_a', left); } var right = expression(140); if ((right.arity === 'number' && (right.value === 0 || right.value === 1)) || right.arity === 'string') { warn('unexpected_a', right); } if (left.arity === right.arity && left.arity === 'number') { left.value /= right.value; left.thru = right.thru; discard(right); discard(that); return left; } that.first = left; that.second = right; return that; }); infix('%', 140, function (left, that) { if ((left.arity === 'number' && (left.value === 0 || left.value === 1)) || left.arity === 'string') { warn('unexpected_a', left); } var right = expression(140); if ((right.arity === 'number' && (right.value === 0 || right.value === 1)) || right.arity === 'string') { warn('unexpected_a', right); } if (left.arity === right.arity && left.arity === 'number') { left.value %= right.value; left.thru = right.thru; discard(right); discard(that); return left; } that.first = left; that.second = right; return that; }); suffix('++'); prefix('++'); suffix('--'); prefix('--'); prefix('delete', function () { one_space(); var p = expression(0); if (!p || (p.id !== '.' && p.id !== '[')) { warn('deleted'); } this.first = p; return this; }); prefix('~', function () { no_space_only(); if (option.bitwise) { warn('unexpected_a', this); } expression(150); return this; }); prefix('!', function () { no_space_only(); this.first = expression(150); this.arity = 'prefix'; if (bang[this.first.id] === true) { warn('confusing_a', this); } return this; }); prefix('typeof'); prefix('new', function () { one_space(); var c = expression(160), i, p; this.first = c; if (c.id !== 'function') { if (c.identifier) { switch (c.value) { case 'Object': warn('use_object', token); break; case 'Array': if (next_token.id === '(') { p = next_token; p.first = this; advance('('); if (next_token.id !== ')') { p.second = expression(0); if (p.second.arity !== 'number' || !p.second.value) { expected_condition(p.second, bundle.use_array); i = false; } else { i = true; } while (next_token.id !== ')' && next_token.id !== '(end)') { if (i) { warn('use_array', p); i = false; } advance(); } } else { warn('use_array', token); } advance(')', p); discard(); return p; } warn('use_array', token); break; case 'Number': case 'String': case 'Boolean': case 'Math': case 'JSON': warn('not_a_constructor', c); break; case 'Function': if (!option.evil) { warn('function_eval'); } break; case 'Date': case 'RegExp': break; default: if (c.id !== 'function') { i = c.value.substr(0, 1); if (option.newcap && (i < 'A' || i > 'Z')) { warn('constructor_name_a', token); } } } } else { if (c.id !== '.' && c.id !== '[' && c.id !== '(') { warn('bad_constructor', token); } } } else { warn('weird_new', this); } if (next_token.id !== '(') { warn('missing_a', next_token, '()'); } return this; }); infix('(', 160, function (left, that) { if (indent && indent.mode === 'expression') { no_space(prev_token, token); } else { no_space_only(prev_token, token); } if (!left.immed && left.id === 'function') { warn('wrap_immediate'); } var p = []; if (left) { if (left.identifier) { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { if (left.value !== 'Number' && left.value !== 'String' && left.value !== 'Boolean' && left.value !== 'Date') { if (left.value === 'Math' || left.value === 'JSON') { warn('not_a_function', left); } else if (left.value === 'Object') { warn('use_object', token); } else if (left.value === 'Array' || option.newcap) { warn('missing_a', left, 'new'); } } } } else if (left.id === '.') { if (option.safe && left.first.value === 'Math' && left.second === 'random') { warn('adsafe', left); } } } step_in(); if (next_token.id !== ')') { no_space(); for (;;) { edge(); p.push(expression(10)); if (next_token.id !== ',') { break; } comma(); } } no_space(); step_out(')', that); if (typeof left === 'object') { if (left.value === 'parseInt' && p.length === 1) { warn('radix', left); } if (!option.evil) { if (left.value === 'eval' || left.value === 'Function' || left.value === 'execScript') { warn('evil', left); } else if (p[0] && p[0].arity === 'string' && (left.value === 'setTimeout' || left.value === 'setInterval')) { warn('implied_evil', left); } } if (!left.identifier && left.id !== '.' && left.id !== '[' && left.id !== '(' && left.id !== '&&' && left.id !== '||' && left.id !== '?') { warn('bad_invocation', left); } } that.first = left; that.second = p; return that; }, true); prefix('(', function () { step_in('expression'); discard(); no_space(); edge(); if (next_token.id === 'function') { next_token.immed = true; } var value = expression(0); value.paren = true; no_space(); step_out(')', this); discard(); if (value.id === 'function') { if (next_token.id === '(') { warn('move_invocation'); } else { warn('bad_wrap', this); } } return value; }); infix('.', 170, function (left, that) { no_space(prev_token, token); no_space(); var name = identifier(); if (typeof name === 'string') { tally_property(name); } that.first = left; that.second = token; if (left && left.value === 'arguments' && (name === 'callee' || name === 'caller')) { warn('avoid_a', left, 'arguments.' + name); } else if (!option.evil && left && left.value === 'document' && (name === 'write' || name === 'writeln')) { warn('write_is_wrong', left); } else if (option.adsafe) { if (!adsafe_top && left.value === 'ADSAFE') { if (name === 'id' || name === 'lib') { warn('adsafe', that); } else if (name === 'go') { if (xmode !== 'script') { warn('adsafe', that); } else if (adsafe_went || next_token.id !== '(' || peek(0).arity !== 'string' || peek(0).value !== adsafe_id || peek(1).id !== ',') { fail('adsafe_a', that, 'go'); } adsafe_went = true; adsafe_may = false; } } adsafe_top = false; } if (!option.evil && (name === 'eval' || name === 'execScript')) { warn('evil'); } else if (option.safe) { for (;;) { if (banned[name] === true) { warn('adsafe_a', token, name); } if (typeof predefined[left.value] !== 'boolean' || next_token.id === '(') { break; } if (standard_property[name] === true) { if (next_token.id === '.') { warn('adsafe', that); } break; } if (next_token.id !== '.') { warn('adsafe', that); break; } advance('.'); token.first = that; token.second = name; that = token; name = identifier(); if (typeof name === 'string') { tally_property(name); } } } return that; }, true); infix('[', 170, function (left, that) { no_space_only(prev_token, token); no_space(); step_in(); edge(); var e = expression(0), s; if (e.arity === 'string') { if (option.safe && banned[e.value] === true) { warn('adsafe_a', e); } else if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) { warn('evil', e); } else if (option.safe && (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) { warn('adsafe_subscript_a', e); } tally_property(e.value); if (!option.sub && ix.test(e.value)) { s = syntax[e.value]; if (!s || !s.reserved) { warn('subscript', e); } } } else if (e.arity !== 'number' || e.value < 0) { if (option.safe) { warn('adsafe_subscript_a', e); } } step_out(']', that); discard(); no_space(prev_token, token); that.first = left; that.second = e; return that; }, true); prefix('[', function () { this.arity = 'prefix'; this.first = []; step_in('array'); while (next_token.id !== '(end)') { while (next_token.id === ',') { warn('unexpected_a', next_token); advance(','); discard(); } if (next_token.id === ']') { break; } edge(); this.first.push(expression(10)); if (next_token.id === ',') { comma(); if (next_token.id === ']' && !option.es5) { warn('unexpected_a', token); break; } } else { break; } } step_out(']', this); discard(); return this; }, 170); function property_name() { var id = optional_identifier(true); if (!id) { if (next_token.arity === 'string') { id = next_token.value; if (option.safe) { if (banned[id]) { warn('adsafe_a'); } else if (id.charAt(0) === '_' || id.charAt(id.length - 1) === '_') { warn('dangling_a'); } } advance(); } else if (next_token.arity === 'number') { id = next_token.value.toString(); advance(); } } return id; } function function_params() { var id, paren = next_token, params = []; advance('('); step_in(); discard(); no_space(); if (next_token.id === ')') { no_space(); step_out(')', paren); discard(); return; } for (;;) { edge(); id = identifier(); params.push(token); add_label(id, 'parameter'); if (next_token.id === ',') { comma(); } else { no_space(); step_out(')', paren); discard(); return params; } } } function do_function(func, name) { var old_properties = properties, old_option = option, old_global = global, old_scope = scope; funct = { '(name)' : name || '"' + anonname + '"', '(line)' : next_token.line, '(context)' : funct, '(breakage)' : 0, '(loopage)' : 0, '(scope)' : scope, '(token)' : func }; properties = old_properties && Object.create(old_properties); option = Object.create(old_option); global = Object.create(old_global); scope = Object.create(old_scope); token.funct = funct; functions.push(funct); if (name) { add_label(name, 'function'); } func.name = name || ''; func.first = funct['(params)'] = function_params(); one_space(); func.block = block(false); funct = funct['(context)']; properties = old_properties; option = old_option; global = old_global; scope = old_scope; } prefix('{', function () { var get, i, j, name, p, set, seen = {}; this.arity = 'prefix'; this.first = []; step_in(); while (next_token.id !== '}') { // JSLint recognizes the ES5 extension for get/set in object literals, // but requires that they be used in pairs. edge(); if (next_token.value === 'get' && peek().id !== ':') { if (!option.es5) { warn('get_set'); } get = next_token; advance('get'); one_space_only(); name = next_token; i = property_name(); if (!i) { fail('missing_property'); } do_function(get, ''); if (funct['(loopage)']) { warn('function_loop', get); } p = get.first; if (p) { warn('parameter_a_get_b', p[0], p[0].value, i); } comma(); set = next_token; spaces(); edge(); advance('set'); one_space_only(); j = property_name(); if (i !== j) { fail('expected_a_b', token, i, j || next_token.value); } do_function(set, ''); p = set.first; if (!p || p.length !== 1) { fail('parameter_set_a', set, 'value'); } else if (p[0].value !== 'value') { fail('expected_a_b', p[0], 'value', p[0].value); } name.first = [get, set]; } else { name = next_token; i = property_name(); if (typeof i !== 'string') { fail('missing_property'); } advance(':'); discard(); spaces(); name.first = expression(10); } this.first.push(name); if (seen[i] === true) { warn('duplicate_a', next_token, i); } seen[i] = true; tally_property(i); if (next_token.id !== ',') { break; } for (;;) { comma(); if (next_token.id !== ',') { break; } warn('unexpected_a', next_token); } if (next_token.id === '}' && !option.es5) { warn('unexpected_a', token); } } step_out('}', this); discard(); return this; }); stmt('{', function () { discard(); warn('statement_block'); this.arity = 'statement'; this.block = statements(); this.disrupt = this.block.disrupt; advance('}', this); discard(); return this; }); stmt('/*global', directive); stmt('/*jslint', directive); stmt('/*members', directive); stmt('/*properties', directive); stmt('var', function () { // JavaScript does not have block scope. It only has function scope. So, // declaring a variable in a block can have unexpected consequences. // var.first will contain an array, the array containing name tokens // and assignment tokens. var assign, id, name; if (funct['(onevar)'] && option.onevar) { warn('combine_var'); } else if (!funct['(global)']) { funct['(onevar)'] = true; } this.arity = 'statement'; this.first = []; step_in('var'); for (;;) { name = next_token; id = identifier(); if (funct['(global)'] && predefined[id] === false) { warn('redefinition_a', token, id); } add_label(id, 'error'); if (next_token.id === '=') { assign = next_token; assign.first = name; spaces(); advance('='); spaces(); if (next_token.id === 'undefined') { warn('unnecessary_initialize', token, id); } if (peek(0).id === '=' && next_token.identifier) { fail('var_a_not'); } assign.second = expression(0); assign.arity = 'infix'; this.first.push(assign); } else { this.first.push(name); } funct[id] = 'unused'; if (next_token.id !== ',') { break; } comma(); if (var_mode && next_token.line === token.line && this.first.length === 1) { var_mode = false; indent.open = false; indent.at -= option.indent; } spaces(); edge(); } var_mode = false; step_out(); return this; }); stmt('function', function () { one_space(); if (in_block) { warn('function_block', token); } var i = identifier(); if (i) { add_label(i, 'unction'); no_space(); } do_function(this, i, true); if (next_token.id === '(' && next_token.line === token.line) { fail('function_statement'); } this.arity = 'statement'; return this; }); prefix('function', function () { one_space(); var i = optional_identifier(); if (i) { no_space(); } do_function(this, i); if (funct['(loopage)']) { warn('function_loop'); } this.arity = 'function'; return this; }); stmt('if', function () { var paren = next_token; one_space(); advance('('); step_in('control'); discard(); no_space(); edge(); this.arity = 'statement'; this.first = expected_condition(expected_relation(expression(0))); no_space(); step_out(')', paren); discard(); one_space(); this.block = block(true); if (next_token.id === 'else') { one_space(); advance('else'); discard(); one_space(); this['else'] = next_token.id === 'if' || next_token.id === 'switch' ? statement(true) : block(true); if (this['else'].disrupt && this.block.disrupt) { this.disrupt = true; } } return this; }); stmt('try', function () { // try.first The catch variable // try.second The catch clause // try.third The finally clause // try.block The try block var exception_variable, old_scope, paren; if (option.adsafe) { warn('adsafe_a', this); } one_space(); this.arity = 'statement'; this.block = block(false); if (next_token.id === 'catch') { one_space(); advance('catch'); discard(); one_space(); paren = next_token; advance('('); step_in('control'); discard(); no_space(); edge(); old_scope = scope; scope = Object.create(old_scope); exception_variable = next_token.value; this.first = exception_variable; if (!next_token.identifier) { warn('expected_identifier_a', next_token); } else { add_label(exception_variable, 'exception'); } advance(); no_space(); step_out(')', paren); discard(); one_space(); this.second = block(false); scope = old_scope; } if (next_token.id === 'finally') { discard(); one_space(); advance('finally'); discard(); one_space(); this.third = block(false); } else if (!this.second) { fail('expected_a_b', next_token, 'catch', next_token.value); } return this; }); labeled_stmt('while', function () { one_space(); var paren = next_token; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); step_in('control'); discard(); no_space(); edge(); this.arity = 'statement'; this.first = expected_relation(expression(0)); if (this.first.id !== 'true') { expected_condition(this.first, bundle.unexpected_a); } no_space(); step_out(')', paren); discard(); one_space(); this.block = block(true); if (this.block.disrupt) { warn('strange_loop', prev_token); } funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }); reserve('with'); labeled_stmt('switch', function () { // switch.first the switch expression // switch.second the array of cases. A case is 'case' or 'default' token: // case.first the array of case expressions // case.second the array of statements // If all of the arrays of statements are disrupt, then the switch is disrupt. var particular, the_case = next_token, unbroken = true; funct['(breakage)'] += 1; one_space(); advance('('); discard(); no_space(); step_in(); this.arity = 'statement'; this.first = expected_condition(expected_relation(expression(0))); no_space(); step_out(')', the_case); discard(); one_space(); advance('{'); step_in(); this.second = []; while (next_token.id === 'case') { the_case = next_token; the_case.first = []; spaces(); edge('case'); advance('case'); for (;;) { one_space(); particular = expression(0); the_case.first.push(particular); if (particular.id === 'NaN') { warn('unexpected_a', particular); } no_space_only(); advance(':'); discard(); if (next_token.id !== 'case') { break; } spaces(); edge('case'); advance('case'); discard(); } spaces(); the_case.second = statements(); if (the_case.second && the_case.second.length > 0) { particular = the_case.second[the_case.second.length - 1]; if (particular.disrupt) { if (particular.id === 'break') { unbroken = false; } } else { warn('missing_a_after_b', next_token, 'break', 'case'); } } else { warn('empty_case'); } this.second.push(the_case); } if (this.second.length === 0) { warn('missing_a', next_token, 'case'); } if (next_token.id === 'default') { spaces(); the_case = next_token; edge('case'); advance('default'); discard(); no_space_only(); advance(':'); discard(); spaces(); the_case.second = statements(); if (the_case.second && the_case.second.length > 0) { particular = the_case.second[the_case.second.length - 1]; if (unbroken && particular.disrupt && particular.id !== 'break') { this.disrupt = true; } } this.second.push(the_case); } funct['(breakage)'] -= 1; spaces(); step_out('}', this); return this; }); stmt('debugger', function () { if (!option.debug) { warn('unexpected_a', this); } this.arity = 'statement'; return this; }); labeled_stmt('do', function () { funct['(breakage)'] += 1; funct['(loopage)'] += 1; one_space(); this.arity = 'statement'; this.block = block(true); if (this.block.disrupt) { warn('strange_loop', prev_token); } one_space(); advance('while'); discard(); var paren = next_token; one_space(); advance('('); step_in(); discard(); no_space(); edge(); this.first = expected_condition(expected_relation(expression(0)), bundle.unexpected_a); no_space(); step_out(')', paren); discard(); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }); labeled_stmt('for', function () { var blok, filter, ok = false, paren = next_token, the_in, value; this.arity = 'statement'; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); step_in('control'); discard(); spaces(this, paren); no_space(); if (next_token.id === 'var') { fail('move_var'); } edge(); if (peek(0).id === 'in') { value = next_token; switch (funct[value.value]) { case 'unused': funct[value.value] = 'var'; break; case 'var': break; default: warn('bad_in_a', value); } advance(); the_in = next_token; advance('in'); the_in.first = value; the_in.second = expression(20); step_out(')', paren); discard(); this.first = the_in; blok = block(true); if (!option.forin) { if (blok.length === 1 && typeof blok[0] === 'object' && blok[0].value === 'if' && !blok[0]['else']) { filter = blok[0].first; while (filter.id === '&&') { filter = filter.first; } switch (filter.id) { case '===': case '!==': ok = filter.first.id === '[' ? ( filter.first.first.value === the_in.second.value && filter.first.second.value === the_in.first.value ) : ( filter.first.id === 'typeof' && filter.first.first.id === '[' && filter.first.first.first.value === the_in.second.value && filter.first.first.second.value === the_in.first.value ); break; case '(': ok = filter.first.id === '.' && (( filter.first.first.value === the_in.second.value && filter.first.second.value === 'hasOwnProperty' && filter.second[0].value === the_in.first.value ) || ( filter.first.first.value === 'ADSAFE' && filter.first.second.value === 'has' && filter.second[0].value === the_in.second.value && filter.second[1].value === the_in.first.value ) || ( filter.first.first.id === '.' && filter.first.first.first.id === '.' && filter.first.first.first.first.value === 'Object' && filter.first.first.first.second.value === 'prototype' && filter.first.first.second.value === 'hasOwnProperty' && filter.first.second.value === 'call' && filter.second[0].value === the_in.second.value && filter.second[1].value === the_in.first.value )); break; } } if (!ok) { warn('for_if', this); } } } else { if (next_token.id !== ';') { edge(); this.first = []; for (;;) { this.first.push(expression(0, 'for')); if (next_token.id !== ',') { break; } comma(); } } semicolon(); if (next_token.id !== ';') { edge(); this.second = expected_relation(expression(0)); if (this.second.id !== 'true') { expected_condition(this.second, bundle.unexpected_a); } } semicolon(token); if (next_token.id === ';') { fail('expected_a_b', next_token, ')', ';'); } if (next_token.id !== ')') { this.third = []; edge(); for (;;) { this.third.push(expression(0, 'for')); if (next_token.id !== ',') { break; } comma(); } } no_space(); step_out(')', paren); discard(); one_space(); blok = block(true); } if (blok.disrupt) { warn('strange_loop', prev_token); } this.block = blok; funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }); disrupt_stmt('break', function () { var label = next_token.value; this.arity = 'statement'; if (funct['(breakage)'] === 0) { warn('unexpected_a', this); } if (next_token.identifier && token.line === next_token.line) { one_space_only(); if (funct[label] !== 'label') { warn('not_a_label', next_token); } else if (scope[label] !== funct) { warn('not_a_scope', next_token); } this.first = next_token; advance(); } return this; }); disrupt_stmt('continue', function () { if (!option['continue']) { warn('unexpected_a', this); } var label = next_token.value; this.arity = 'statement'; if (funct['(breakage)'] === 0) { warn('unexpected_a', this); } if (next_token.identifier && token.line === next_token.line) { one_space_only(); if (funct[label] !== 'label') { warn('not_a_label', next_token); } else if (scope[label] !== funct) { warn('not_a_scope', next_token); } this.first = next_token; advance(); } return this; }); disrupt_stmt('return', function () { this.arity = 'statement'; if (next_token.id !== ';' && next_token.line === token.line) { one_space_only(); if (next_token.id === '/' || next_token.id === '(regexp)') { warn('wrap_regexp'); } this.first = expression(20); } return this; }); disrupt_stmt('throw', function () { this.arity = 'statement'; one_space_only(); this.first = expression(20); return this; }); // Superfluous reserved words reserve('class'); reserve('const'); reserve('enum'); reserve('export'); reserve('extends'); reserve('import'); reserve('super'); // Harmony reserved words reserve('let'); reserve('yield'); reserve('implements'); reserve('interface'); reserve('package'); reserve('private'); reserve('protected'); reserve('public'); reserve('static'); // Parse JSON function json_value() { function json_object() { var brace = next_token, object = {}; advance('{'); if (next_token.id !== '}') { while (next_token.id !== '(end)') { while (next_token.id === ',') { warn('unexpected_a', next_token); comma(); } if (next_token.arity !== 'string') { warn('expected_string_a'); } if (object[next_token.value] === true) { warn('duplicate_a'); } else if (next_token.value === '__proto__') { warn('dangling_a'); } else { object[next_token.value] = true; } advance(); advance(':'); json_value(); if (next_token.id !== ',') { break; } comma(); if (next_token.id === '}') { warn('unexpected_a', token); break; } } } advance('}', brace); } function json_array() { var bracket = next_token; advance('['); if (next_token.id !== ']') { while (next_token.id !== '(end)') { while (next_token.id === ',') { warn('unexpected_a', next_token); comma(); } json_value(); if (next_token.id !== ',') { break; } comma(); if (next_token.id === ']') { warn('unexpected_a', token); break; } } } advance(']', bracket); } switch (next_token.id) { case '{': json_object(); break; case '[': json_array(); break; case 'true': case 'false': case 'null': case '(number)': case '(string)': advance(); break; case '-': advance('-'); no_space_only(); advance('(number)'); break; default: fail('unexpected_a'); } } // CSS parsing. function css_name() { if (next_token.identifier) { advance(); return true; } } function css_number() { if (next_token.id === '-') { advance('-'); no_space_only(); } if (next_token.arity === 'number') { advance('(number)'); return true; } } function css_string() { if (next_token.arity === 'string') { advance(); return true; } } function css_color() { var i, number, paren, value; if (next_token.identifier) { value = next_token.value; if (value === 'rgb' || value === 'rgba') { advance(); paren = next_token; advance('('); for (i = 0; i < 3; i += 1) { if (i) { comma(); } number = next_token.value; if (next_token.arity !== 'number' || number < 0) { warn('expected_positive_a', next_token); advance(); } else { advance(); if (next_token.id === '%') { advance('%'); if (number > 100) { warn('expected_percent_a', token, number); } } else { if (number > 255) { warn('expected_small_a', token, number); } } } } if (value === 'rgba') { comma(); number = +next_token.value; if (next_token.arity !== 'number' || number < 0 || number > 1) { warn('expected_fraction_a', next_token); } advance(); if (next_token.id === '%') { warn('unexpected_a'); advance('%'); } } advance(')', paren); return true; } else if (css_colorData[next_token.value] === true) { advance(); return true; } } else if (next_token.id === '(color)') { advance(); return true; } return false; } function css_length() { if (next_token.id === '-') { advance('-'); no_space_only(); } if (next_token.arity === 'number') { advance(); if (next_token.arity !== 'string' && css_lengthData[next_token.value] === true) { no_space_only(); advance(); } else if (+token.value !== 0) { warn('expected_linear_a'); } return true; } return false; } function css_line_height() { if (next_token.id === '-') { advance('-'); no_space_only(); } if (next_token.arity === 'number') { advance(); if (next_token.arity !== 'string' && css_lengthData[next_token.value] === true) { no_space_only(); advance(); } return true; } return false; } function css_width() { if (next_token.identifier) { switch (next_token.value) { case 'thin': case 'medium': case 'thick': advance(); return true; } } else { return css_length(); } } function css_margin() { if (next_token.identifier) { if (next_token.value === 'auto') { advance(); return true; } } else { return css_length(); } } function css_attr() { if (next_token.identifier && next_token.value === 'attr') { advance(); advance('('); if (!next_token.identifier) { warn('expected_name_a'); } advance(); advance(')'); return true; } return false; } function css_comma_list() { while (next_token.id !== ';') { if (!css_name() && !css_string()) { warn('expected_name_a'); } if (next_token.id !== ',') { return true; } comma(); } } function css_counter() { if (next_token.identifier && next_token.value === 'counter') { advance(); advance('('); advance(); if (next_token.id === ',') { comma(); if (next_token.arity !== 'string') { warn('expected_string_a'); } advance(); } advance(')'); return true; } if (next_token.identifier && next_token.value === 'counters') { advance(); advance('('); if (!next_token.identifier) { warn('expected_name_a'); } advance(); if (next_token.id === ',') { comma(); if (next_token.arity !== 'string') { warn('expected_string_a'); } advance(); } if (next_token.id === ',') { comma(); if (next_token.arity !== 'string') { warn('expected_string_a'); } advance(); } advance(')'); return true; } return false; } function css_shape() { var i; if (next_token.identifier && next_token.value === 'rect') { advance(); advance('('); for (i = 0; i < 4; i += 1) { if (!css_length()) { warn('expected_number_a'); break; } } advance(')'); return true; } return false; } function css_url() { var c, url; if (next_token.identifier && next_token.value === 'url') { next_token = lex.range('(', ')'); url = next_token.value; c = url.charAt(0); if (c === '"' || c === '\'') { if (url.slice(-1) !== c) { warn('bad_url'); } else { url = url.slice(1, -1); if (url.indexOf(c) >= 0) { warn('bad_url'); } } } if (!url) { warn('missing_url'); } if (option.safe && ux.test(url)) { fail('adsafe_a', next_token, url); } urls.push(url); advance(); return true; } return false; } css_any = [css_url, function () { for (;;) { if (next_token.identifier) { switch (next_token.value.toLowerCase()) { case 'url': css_url(); break; case 'expression': warn('unexpected_a'); advance(); break; default: advance(); } } else { if (next_token.id === ';' || next_token.id === '!' || next_token.id === '(end)' || next_token.id === '}') { return true; } advance(); } } }]; css_border_style = [ 'none', 'dashed', 'dotted', 'double', 'groove', 'hidden', 'inset', 'outset', 'ridge', 'solid' ]; css_break = [ 'auto', 'always', 'avoid', 'left', 'right' ]; css_media = { 'all': true, 'braille': true, 'embossed': true, 'handheld': true, 'print': true, 'projection': true, 'screen': true, 'speech': true, 'tty': true, 'tv': true }; css_overflow = [ 'auto', 'hidden', 'scroll', 'visible' ]; css_attribute_data = { background: [ true, 'background-attachment', 'background-color', 'background-image', 'background-position', 'background-repeat' ], 'background-attachment': ['scroll', 'fixed'], 'background-color': ['transparent', css_color], 'background-image': ['none', css_url], 'background-position': [ 2, [css_length, 'top', 'bottom', 'left', 'right', 'center'] ], 'background-repeat': [ 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' ], 'border': [true, 'border-color', 'border-style', 'border-width'], 'border-bottom': [ true, 'border-bottom-color', 'border-bottom-style', 'border-bottom-width' ], 'border-bottom-color': css_color, 'border-bottom-style': css_border_style, 'border-bottom-width': css_width, 'border-collapse': ['collapse', 'separate'], 'border-color': ['transparent', 4, css_color], 'border-left': [ true, 'border-left-color', 'border-left-style', 'border-left-width' ], 'border-left-color': css_color, 'border-left-style': css_border_style, 'border-left-width': css_width, 'border-right': [ true, 'border-right-color', 'border-right-style', 'border-right-width' ], 'border-right-color': css_color, 'border-right-style': css_border_style, 'border-right-width': css_width, 'border-spacing': [2, css_length], 'border-style': [4, css_border_style], 'border-top': [ true, 'border-top-color', 'border-top-style', 'border-top-width' ], 'border-top-color': css_color, 'border-top-style': css_border_style, 'border-top-width': css_width, 'border-width': [4, css_width], bottom: [css_length, 'auto'], 'caption-side' : ['bottom', 'left', 'right', 'top'], clear: ['both', 'left', 'none', 'right'], clip: [css_shape, 'auto'], color: css_color, content: [ 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', css_string, css_url, css_counter, css_attr ], 'counter-increment': [ css_name, 'none' ], 'counter-reset': [ css_name, 'none' ], cursor: [ css_url, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait' ], direction: ['ltr', 'rtl'], display: [ 'block', 'compact', 'inline', 'inline-block', 'inline-table', 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row', 'table-row-group' ], 'empty-cells': ['show', 'hide'], 'float': ['left', 'none', 'right'], font: [ 'caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar', true, 'font-size', 'font-style', 'font-weight', 'font-family' ], 'font-family': css_comma_list, 'font-size': [ 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'larger', 'smaller', css_length ], 'font-size-adjust': ['none', css_number], 'font-stretch': [ 'normal', 'wider', 'narrower', 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'semi-expanded', 'expanded', 'extra-expanded' ], 'font-style': [ 'normal', 'italic', 'oblique' ], 'font-variant': [ 'normal', 'small-caps' ], 'font-weight': [ 'normal', 'bold', 'bolder', 'lighter', css_number ], height: [css_length, 'auto'], left: [css_length, 'auto'], 'letter-spacing': ['normal', css_length], 'line-height': ['normal', css_line_height], 'list-style': [ true, 'list-style-image', 'list-style-position', 'list-style-type' ], 'list-style-image': ['none', css_url], 'list-style-position': ['inside', 'outside'], 'list-style-type': [ 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', 'hiragana-iroha', 'katakana-oroha', 'none' ], margin: [4, css_margin], 'margin-bottom': css_margin, 'margin-left': css_margin, 'margin-right': css_margin, 'margin-top': css_margin, 'marker-offset': [css_length, 'auto'], 'max-height': [css_length, 'none'], 'max-width': [css_length, 'none'], 'min-height': css_length, 'min-width': css_length, opacity: css_number, outline: [true, 'outline-color', 'outline-style', 'outline-width'], 'outline-color': ['invert', css_color], 'outline-style': [ 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', 'outset', 'ridge', 'solid' ], 'outline-width': css_width, overflow: css_overflow, 'overflow-x': css_overflow, 'overflow-y': css_overflow, padding: [4, css_length], 'padding-bottom': css_length, 'padding-left': css_length, 'padding-right': css_length, 'padding-top': css_length, 'page-break-after': css_break, 'page-break-before': css_break, position: ['absolute', 'fixed', 'relative', 'static'], quotes: [8, css_string], right: [css_length, 'auto'], 'table-layout': ['auto', 'fixed'], 'text-align': ['center', 'justify', 'left', 'right'], 'text-decoration': [ 'none', 'underline', 'overline', 'line-through', 'blink' ], 'text-indent': css_length, 'text-shadow': ['none', 4, [css_color, css_length]], 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], top: [css_length, 'auto'], 'unicode-bidi': ['normal', 'embed', 'bidi-override'], 'vertical-align': [ 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', 'text-bottom', css_length ], visibility: ['visible', 'hidden', 'collapse'], 'white-space': [ 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' ], width: [css_length, 'auto'], 'word-spacing': ['normal', css_length], 'word-wrap': ['break-word', 'normal'], 'z-index': ['auto', css_number] }; function style_attribute() { var v; while (next_token.id === '*' || next_token.id === '#' || next_token.value === '_') { if (!option.css) { warn('unexpected_a'); } advance(); } if (next_token.id === '-') { if (!option.css) { warn('unexpected_a'); } advance('-'); if (!next_token.identifier) { warn('expected_nonstandard_style_attribute'); } advance(); return css_any; } else { if (!next_token.identifier) { warn('expected_style_attribute'); } else { if (Object.prototype.hasOwnProperty.call(css_attribute_data, next_token.value)) { v = css_attribute_data[next_token.value]; } else { v = css_any; if (!option.css) { warn('unrecognized_style_attribute_a'); } } } advance(); return v; } } function style_value(v) { var i = 0, n, once, match, round, start = 0, vi; switch (typeof v) { case 'function': return v(); case 'string': if (next_token.identifier && next_token.value === v) { advance(); return true; } return false; } for (;;) { if (i >= v.length) { return false; } vi = v[i]; i += 1; if (vi === true) { break; } else if (typeof vi === 'number') { n = vi; vi = v[i]; i += 1; } else { n = 1; } match = false; while (n > 0) { if (style_value(vi)) { match = true; n -= 1; } else { break; } } if (match) { return true; } } start = i; once = []; for (;;) { round = false; for (i = start; i < v.length; i += 1) { if (!once[i]) { if (style_value(css_attribute_data[v[i]])) { match = true; round = true; once[i] = true; break; } } } if (!round) { return match; } } } function style_child() { if (next_token.arity === 'number') { advance(); if (next_token.value === 'n' && next_token.identifier) { no_space_only(); advance(); if (next_token.id === '+') { no_space_only(); advance('+'); no_space_only(); advance('(number)'); } } return; } else { if (next_token.identifier && (next_token.value === 'odd' || next_token.value === 'even')) { advance(); return; } } warn('unexpected_a'); } function substyle() { var v; for (;;) { if (next_token.id === '}' || next_token.id === '(end)' || (xquote && next_token.id === xquote)) { return; } while (next_token.id === ';') { warn('unexpected_a'); semicolon(); } v = style_attribute(); advance(':'); if (next_token.identifier && next_token.value === 'inherit') { advance(); } else { if (!style_value(v)) { warn('unexpected_a'); advance(); } } if (next_token.id === '!') { advance('!'); no_space_only(); if (next_token.identifier && next_token.value === 'important') { advance(); } else { warn('expected_a_b', next_token, 'important', next_token.value); } } if (next_token.id === '}' || next_token.id === xquote) { warn('expected_a_b', next_token, ';', next_token.value); } else { semicolon(); } } } function style_selector() { if (next_token.identifier) { if (!Object.prototype.hasOwnProperty.call(html_tag, option.cap ? next_token.value.toLowerCase() : next_token.value)) { warn('expected_tagname_a'); } advance(); } else { switch (next_token.id) { case '>': case '+': advance(); style_selector(); break; case ':': advance(':'); switch (next_token.value) { case 'active': case 'after': case 'before': case 'checked': case 'disabled': case 'empty': case 'enabled': case 'first-child': case 'first-letter': case 'first-line': case 'first-of-type': case 'focus': case 'hover': case 'last-child': case 'last-of-type': case 'link': case 'only-of-type': case 'root': case 'target': case 'visited': advance(); break; case 'lang': advance(); advance('('); if (!next_token.identifier) { warn('expected_lang_a'); } advance(')'); break; case 'nth-child': case 'nth-last-child': case 'nth-last-of-type': case 'nth-of-type': advance(); advance('('); style_child(); advance(')'); break; case 'not': advance(); advance('('); if (next_token.id === ':' && peek(0).value === 'not') { warn('not'); } style_selector(); advance(')'); break; default: warn('expected_pseudo_a'); } break; case '#': advance('#'); if (!next_token.identifier) { warn('expected_id_a'); } advance(); break; case '*': advance('*'); break; case '.': advance('.'); if (!next_token.identifier) { warn('expected_class_a'); } advance(); break; case '[': advance('['); if (!next_token.identifier) { warn('expected_attribute_a'); } advance(); if (next_token.id === '=' || next_token.value === '~=' || next_token.value === '$=' || next_token.value === '|=' || next_token.id === '*=' || next_token.id === '^=') { advance(); if (next_token.arity !== 'string') { warn('expected_string_a'); } advance(); } advance(']'); break; default: fail('expected_selector_a'); } } } function style_pattern() { if (next_token.id === '{') { warn('expected_style_pattern'); } for (;;) { style_selector(); if (next_token.id === '= 0) { warn('unexpected_char_a_b', token, v.charAt(x), a); } ids[u] = true; } else if (a === 'class' || a === 'type' || a === 'name') { x = v.search(qx); if (x >= 0) { warn('unexpected_char_a_b', token, v.charAt(x), a); } ids[u] = true; } else if (a === 'href' || a === 'background' || a === 'content' || a === 'data' || a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { if (option.safe && ux.test(v)) { fail('bad_url', next_token, v); } urls.push(v); } else if (a === 'for') { if (option.adsafe) { if (adsafe_id) { if (v.slice(0, adsafe_id.length) !== adsafe_id) { warn('adsafe_prefix_a', next_token, adsafe_id); } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { warn('adsafe_bad_id'); } } else { warn('adsafe_bad_id'); } } } else if (a === 'name') { if (option.adsafe && v.indexOf('_') >= 0) { warn('adsafe_name_a', next_token, v); } } } function do_tag(name, attribute) { var i, tag = html_tag[name], script, x; src = false; if (!tag) { fail( bundle.unrecognized_tag_a, next_token, name === name.toLowerCase() ? name : name + ' (capitalization error)' ); } if (stack.length > 0) { if (name === 'html') { fail('unexpected_a', token, name); } x = tag.parent; if (x) { if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { fail('tag_a_in_b', token, name, x); } } else if (!option.adsafe && !option.fragment) { i = stack.length; do { if (i <= 0) { fail('tag_a_in_b', token, name, 'body'); } i -= 1; } while (stack[i].name !== 'body'); } } switch (name) { case 'div': if (option.adsafe && stack.length === 1 && !adsafe_id) { warn('adsafe_missing_id'); } break; case 'script': xmode = 'script'; advance('>'); if (attribute.lang) { warn('lang', token); } if (option.adsafe && stack.length !== 1) { warn('adsafe_placement', token); } if (attribute.src) { if (option.adsafe && (!adsafe_may || !approved[attribute.src])) { warn('adsafe_source', token); } if (attribute.type) { warn('type', token); } } else { step_in(next_token.from); edge(); use_strict(); adsafe_top = true; script = statements(); // JSLint is also the static analyzer for ADsafe. See www.ADsafe.org. if (option.adsafe) { if (adsafe_went) { fail('adsafe_script', token); } if (script.length !== 1 || aint(script[0], 'id', '(') || aint(script[0].first, 'id', '.') || aint(script[0].first.first, 'value', 'ADSAFE') || aint(script[0].second[0], 'value', adsafe_id)) { fail('adsafe_id_go'); } switch (script[0].first.second.value) { case 'id': if (adsafe_may || script[0].second.length !== 1) { fail('adsafe_id', next_token); } adsafe_may = true; break; case 'go': if (!adsafe_may) { fail('adsafe_id'); } if (script[0].second.length !== 2 || aint(script[0].second[1], 'id', 'function') || script[0].second[1].first.length !== 2 || aint(script[0].second[1].first[0], 'value', 'dom') || aint(script[0].second[1].first[1], 'value', 'lib')) { fail('adsafe_go', next_token); } adsafe_went = true; break; default: fail('adsafe_id_go'); } } indent = null; } xmode = 'html'; advance(''); styles(); xmode = 'html'; advance(''; } function html() { var attribute, attributes, is_empty, name, old_white = option.white, quote, tag_name, tag, wmode; xmode = 'html'; xquote = ''; stack = null; for (;;) { switch (next_token.value) { case '<': xmode = 'html'; advance('<'); attributes = {}; tag_name = next_token; if (!tag_name.identifier) { warn('bad_name_a', tag_name); } name = tag_name.value; if (option.cap) { name = name.toLowerCase(); } tag_name.name = name; advance(); if (!stack) { stack = []; do_begin(name); } tag = html_tag[name]; if (typeof tag !== 'object') { fail('unrecognized_tag_a', tag_name, name); } is_empty = tag.empty; tag_name.type = name; for (;;) { if (next_token.id === '/') { advance('/'); if (next_token.id !== '>') { warn('expected_a_b', next_token, '>', next_token.value); } break; } if (next_token.id && next_token.id.substr(0, 1) === '>') { break; } if (!next_token.identifier) { if (next_token.id === '(end)' || next_token.id === '(error)') { warn('expected_a_b', next_token, '>', next_token.value); } warn('bad_name_a'); } option.white = true; spaces(); attribute = next_token.value; option.white = old_white; advance(); if (!option.cap && attribute !== attribute.toLowerCase()) { warn('attribute_case_a', token); } attribute = attribute.toLowerCase(); xquote = ''; if (Object.prototype.hasOwnProperty.call(attributes, attribute)) { warn('duplicate_a', token, attribute); } if (attribute.slice(0, 2) === 'on') { if (!option.on) { warn('html_handlers'); } xmode = 'scriptstring'; advance('='); quote = next_token.id; if (quote !== '"' && quote !== '\'') { fail('expected_a_b', next_token, '"', next_token.value); } xquote = quote; wmode = option.white; option.white = false; advance(quote); use_strict(); statements(); option.white = wmode; if (next_token.id !== quote) { fail('expected_a_b', next_token, quote, next_token.value); } xmode = 'html'; xquote = ''; advance(quote); tag = false; } else if (attribute === 'style') { xmode = 'scriptstring'; advance('='); quote = next_token.id; if (quote !== '"' && quote !== '\'') { fail('expected_a_b', next_token, '"', next_token.value); } xmode = 'styleproperty'; xquote = quote; advance(quote); substyle(); xmode = 'html'; xquote = ''; advance(quote); tag = false; } else { if (next_token.id === '=') { advance('='); tag = next_token.value; if (!next_token.identifier && next_token.id !== '"' && next_token.id !== '\'' && next_token.arity !== 'string' && next_token.arity !== 'number' && next_token.id !== '(color)') { warn('expected_attribute_value_a', token, attribute); } advance(); } else { tag = true; } } attributes[attribute] = tag; do_attribute(name, attribute, tag); } do_tag(name, attributes); if (!is_empty) { stack.push(tag_name); } xmode = 'outer'; advance('>'); break; case '') { fail('expected_a_b', next_token, '>', next_token.value); } xmode = 'outer'; advance('>'); break; case '' || next_token.id === '(end)') { break; } if (next_token.value.indexOf('--') >= 0) { fail('unexpected_a', next_token, '--'); } if (next_token.value.indexOf('<') >= 0) { fail('unexpected_a', next_token, '<'); } if (next_token.value.indexOf('>') >= 0) { fail('unexpected_a', next_token, '>'); } } xmode = 'outer'; advance('>'); break; case '(end)': return; default: if (next_token.id === '(end)') { fail('missing_a', next_token, ''); } else { advance(); } } if (stack && stack.length === 0 && (option.adsafe || !option.fragment || next_token.id === '(end)')) { break; } } if (next_token.id !== '(end)') { fail('unexpected_a'); } } // The actual JSLINT function itself. var itself = function (the_source, the_option) { var i, keys, predef; JSLINT.comments = []; JSLINT.errors = []; JSLINT.tree = ''; begin = older_token = prev_token = token = next_token = Object.create(syntax['(begin)']); predefined = Object.create(standard); if (the_option) { option = Object.create(the_option); predef = option.predef; if (predef) { if (Array.isArray(predef)) { for (i = 0; i < predef.length; i += 1) { predefined[predef[i]] = true; } } else if (typeof predef === 'object') { keys = Object.keys(predef); for (i = 0; i < keys.length; i += 1) { predefined[keys[i]] = !!predef[keys]; } } } if (option.adsafe) { option.safe = true; } if (option.safe) { option.browser = option['continue'] = option.css = option.debug = option.devel = option.evil = option.forin = option.on = option.rhino = option.sub = option.widget = option.windows = false; option.nomen = option.strict = option.undef = true; predefined.Date = predefined['eval'] = predefined.Function = predefined.Object = null; predefined.ADSAFE = predefined.lib = false; } } else { option = {}; } option.indent = +option.indent || 0; option.maxerr = option.maxerr || 50; adsafe_id = ''; adsafe_may = adsafe_top = adsafe_went = false; approved = {}; if (option.approved) { for (i = 0; i < option.approved.length; i += 1) { approved[option.approved[i]] = option.approved[i]; } } else { approved.test = 'test'; } tab = ''; for (i = 0; i < option.indent; i += 1) { tab += ' '; } global = Object.create(predefined); scope = global; funct = { '(global)': true, '(name)': '(global)', '(scope)': scope, '(breakage)': 0, '(loopage)': 0 }; functions = [funct]; comments_off = false; ids = {}; implied = {}; in_block = false; indent = false; json_mode = false; lookahead = []; member = {}; properties = null; prereg = true; src = false; stack = null; strict_mode = false; urls = []; var_mode = false; warnings = 0; xmode = false; lex.init(the_source); assume(); try { advance(); if (next_token.arity === 'number') { fail('unexpected_a'); } else if (next_token.value.charAt(0) === '<') { html(); if (option.adsafe && !adsafe_went) { warn('adsafe_go', this); } } else { switch (next_token.id) { case '{': case '[': json_mode = true; json_value(); break; case '@': case '*': case '#': case '.': case ':': xmode = 'style'; advance(); if (token.id !== '@' || !next_token.identifier || next_token.value !== 'charset' || token.line !== 1 || token.from !== 1) { fail('css'); } advance(); if (next_token.arity !== 'string' && next_token.value !== 'UTF-8') { fail('css'); } advance(); semicolon(); styles(); break; default: if (option.adsafe && option.fragment) { fail('expected_a_b', next_token, '
', next_token.value); } // If the first token is predef semicolon, ignore it. This is sometimes used when // files are intended to be appended to files that may be sloppy. predef sloppy // file may be depending on semicolon insertion on its last line. step_in(1); if (next_token.id === ';') { semicolon(); } if (next_token.value === 'use strict') { warn('function_strict'); use_strict(); } adsafe_top = true; begin.first = statements(); JSLINT.tree = begin; if (option.adsafe && (JSLINT.tree.length !== 1 || aint(JSLINT.tree[0], 'id', '(') || aint(JSLINT.tree[0].first, 'id', '.') || aint(JSLINT.tree[0].first.first, 'value', 'ADSAFE') || aint(JSLINT.tree[0].first.second, 'value', 'lib') || JSLINT.tree[0].second.length !== 2 || JSLINT.tree[0].second[0].arity !== 'string' || aint(JSLINT.tree[0].second[1], 'id', 'function'))) { fail('adsafe_lib'); } if (JSLINT.tree.disrupt) { warn('weird_program', prev_token); } } } indent = null; advance('(end)'); } catch (e) { if (e) { // `~ JSLINT.errors.push({ reason : e.message, line : e.line || next_token.line, character : e.character || next_token.from }, null); } } return JSLINT.errors.length === 0; }; // Data summary. itself.data = function () { var data = {functions: []}, function_data, globals, i, implieds = [], j, kind, members = [], name, the_function, unused = []; if (itself.errors.length) { data.errors = itself.errors; } if (json_mode) { data.json = true; } for (name in implied) { if (Object.prototype.hasOwnProperty.call(implied, name)) { implieds.push({ name: name, line: implied[name] }); } } if (implieds.length > 0) { data.implieds = implieds; } if (urls.length > 0) { data.urls = urls; } globals = Object.keys(functions[0]).filter(function (value) { return value.charAt(0) !== '(' ? value : undefined; }); if (globals.length > 0) { data.globals = globals; } for (i = 1; i < functions.length; i += 1) { the_function = functions[i]; function_data = {}; for (j = 0; j < functionicity.length; j += 1) { function_data[functionicity[j]] = []; } for (name in the_function) { if (Object.prototype.hasOwnProperty.call(the_function, name)) { if (name.charAt(0) !== '(') { kind = the_function[name]; if (kind === 'unction') { kind = 'unused'; } else if (typeof kind === 'boolean') { kind = 'global'; } if (Array.isArray(function_data[kind])) { function_data[kind].push(name); if (kind === 'unused') { unused.push({ name: name, line: the_function['(line)'], 'function': the_function['(name)'] }); } } } } } for (j = 0; j < functionicity.length; j += 1) { if (function_data[functionicity[j]].length === 0) { delete function_data[functionicity[j]]; } } function_data.name = the_function['(name)']; function_data.param = the_function['(params)']; function_data.line = the_function['(line)']; data.functions.push(function_data); } if (unused.length > 0) { data.unused = unused; } members = []; for (name in member) { if (typeof member[name] === 'number') { data.member = member; break; } } return data; }; itself.report = function (errors_only) { var data = itself.data(); var err, evidence, i, j, key, keys, length, mem = '', name, names, output = [], snippets, the_function, warning; function detail(h, array) { var comma_needed, i, singularity; if (array) { output.push('
' + h + ' '); array = array.sort(); for (i = 0; i < array.length; i += 1) { if (array[i] !== singularity) { singularity = array[i]; output.push((comma_needed ? ', ' : '') + singularity); comma_needed = true; } } output.push('
'); } } if (data.errors || data.implieds || data.unused) { err = true; output.push('
Error:'); if (data.errors) { for (i = 0; i < data.errors.length; i += 1) { warning = data.errors[i]; if (warning) { evidence = warning.evidence || ''; output.push('

Problem' + (isFinite(warning.line) ? ' at line ' + warning.line + ' character ' + warning.character : '') + ': ' + warning.reason.entityify() + '

' + (evidence && (evidence.length > 80 ? evidence.slice(0, 77) + '...' : evidence).entityify()) + '

'); } } } if (data.implieds) { snippets = []; for (i = 0; i < data.implieds.length; i += 1) { snippets[i] = '' + data.implieds[i].name + ' ' + data.implieds[i].line + ''; } output.push('

Implied global: ' + snippets.join(', ') + '

'); } if (data.unused) { snippets = []; for (i = 0; i < data.unused.length; i += 1) { snippets[i] = '' + data.unused[i].name + ' ' + data.unused[i].line + ' ' + data.unused[i]['function'] + ''; } output.push('

Unused variable: ' + snippets.join(', ') + '

'); } if (data.json) { output.push('

JSON: bad.

'); } output.push('
'); } if (!errors_only) { output.push('
'); if (data.urls) { detail("URLs
", data.urls, '
'); } if (xmode === 'style') { output.push('

CSS.

'); } else if (data.json && !err) { output.push('

JSON: good.

'); } else if (data.globals) { output.push('
Global ' + data.globals.sort().join(', ') + '
'); } else { output.push('
No new global variables introduced.
'); } for (i = 0; i < data.functions.length; i += 1) { the_function = data.functions[i]; names = []; if (the_function.param) { for (j = 0; j < the_function.param.length; j += 1) { names[j] = the_function.param[j].value; } } output.push('
' + the_function.line + ' ' + (the_function.name || '') + '(' + names.join(', ') + ')
'); detail('Unused', the_function.unused); detail('Closure', the_function.closure); detail('Variable', the_function['var']); detail('Exception', the_function.exception); detail('Outer', the_function.outer); detail('Global', the_function.global); detail('Label', the_function.label); } if (data.member) { keys = Object.keys(data.member); if (keys.length) { keys = keys.sort(); mem = '
/*properties ';
                    length = 13;
                    for (i = 0; i < keys.length; i += 1) {
                        key = keys[i];
                        name = key.name();
                        if (length + name.length > 72) {
                            output.push(mem + '
'); mem = ' '; length = 1; } length += name.length + 2; if (data.member[key] === 1) { name = '' + name + ''; } if (i < keys.length - 1) { name += ', '; } mem += name; } output.push(mem + '
*/
'); } output.push('
'); } } return output.join(''); }; itself.jslint = itself; itself.edition = '2011-03-22'; return itself; }()); pocketlint-0.5.31/pocketlint/contrib/pep8.py0000755000175000017500000013464011700663125021676 0ustar curtiscurtis00000000000000#!/usr/bin/python # pep8.py - Check Python source code formatting, according to PEP 8 # Copyright (C) 2006 Johann C. Rocholl # # 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. """ Check Python source code formatting, according to PEP 8: http://www.python.org/dev/peps/pep-0008/ For usage and a list of options, try this: $ python pep8.py -h This program and its regression test suite live here: http://github.com/jcrocholl/pep8 Groups of errors and warnings: E errors W warnings 100 indentation 200 whitespace 300 blank lines 400 imports 500 line length 600 deprecation 700 statements You can add checks to this program by writing plugins. Each plugin is a simple function that is called for each line of source code, either physical or logical. Physical line: - Raw line of text from the input file. Logical line: - Multi-line statements converted to a single line. - Stripped left and right. - Contents of strings replaced with 'xxx' of same length. - Comments removed. The check function requests physical or logical lines by the name of the first argument: def maximum_line_length(physical_line) def extraneous_whitespace(logical_line) def blank_lines(logical_line, blank_lines, indent_level, line_number) The last example above demonstrates how check plugins can request additional information with extra arguments. All attributes of the Checker object are available. Some examples: lines: a list of the raw lines from the input file tokens: the tokens that contribute to this logical line line_number: line number in the input file blank_lines: blank lines before this one indent_char: first indentation character in this file (' ' or '\t') indent_level: indentation (with tabs expanded to multiples of 8) previous_indent_level: indentation on previous line previous_logical: previous logical line The docstring of each check function shall be the relevant part of text from PEP 8. It is printed if the user enables --show-pep8. Several docstrings contain examples directly from the PEP 8 document. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) These examples are verified automatically when pep8.py is run with the --doctest option. You can add examples for your own check functions. The format is simple: "Okay" or error/warning code followed by colon and space, the rest of the line is example source code. If you put 'r' before the docstring, you can use \n for newline, \t for tab and \s for space. """ __version__ = '0.6.1' import os import sys import re import time import inspect import keyword import tokenize from optparse import OptionParser from fnmatch import fnmatch try: frozenset except NameError: from sets import ImmutableSet as frozenset DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' DEFAULT_IGNORE = 'E24' MAX_LINE_LENGTH = 79 INDENT_REGEX = re.compile(r'([ \t]*)') RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)') SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)') ERRORCODE_REGEX = re.compile(r'[EW]\d{3}') DOCSTRING_REGEX = re.compile(r'u?r?["\']') WHITESPACE_AROUND_OPERATOR_REGEX = \ re.compile('([^\w\s]*)\s*(\t| )\s*([^\w\s]*)') EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') WHITESPACE_AROUND_NAMED_PARAMETER_REGEX = \ re.compile(r'[()]|\s=[^=]|[^=!<>]=\s') WHITESPACE = ' \t' BINARY_OPERATORS = frozenset(['**=', '*=', '+=', '-=', '!=', '<>', '%=', '^=', '&=', '|=', '==', '/=', '//=', '<=', '>=', '<<=', '>>=', '%', '^', '&', '|', '=', '/', '//', '<', '>', '<<']) UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.INDENT, tokenize.DEDENT, tokenize.NEWLINE]) E225NOT_KEYWORDS = (frozenset(keyword.kwlist + ['print']) - frozenset(['False', 'None', 'True'])) BENCHMARK_KEYS = ('directories', 'files', 'logical lines', 'physical lines') options = None args = None ############################################################################## # Plugins (check functions) for physical lines ############################################################################## def tabs_or_spaces(physical_line, indent_char): r""" Never mix tabs and spaces. The most popular way of indenting Python is with spaces only. The second-most popular way is with tabs only. Code indented with a mixture of tabs and spaces should be converted to using spaces exclusively. When invoking the Python command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended! Okay: if a == 0:\n a = 1\n b = 1 E101: if a == 0:\n a = 1\n\tb = 1 """ indent = INDENT_REGEX.match(physical_line).group(1) for offset, char in enumerate(indent): if char != indent_char: return offset, "E101 indentation contains mixed spaces and tabs" def tabs_obsolete(physical_line): r""" For new projects, spaces-only are strongly recommended over tabs. Most editors have features that make this easy to do. Okay: if True:\n return W191: if True:\n\treturn """ indent = INDENT_REGEX.match(physical_line).group(1) if indent.count('\t'): return indent.index('\t'), "W191 indentation contains tabs" def trailing_whitespace(physical_line): r""" JCR: Trailing whitespace is superfluous. FBM: Except when it occurs as part of a blank line (i.e. the line is nothing but whitespace). According to Python docs[1] a line with only whitespace is considered a blank line, and is to be ignored. However, matching a blank line to its indentation level avoids mistakenly terminating a multi-line statement (e.g. class declaration) when pasting code into the standard Python interpreter. [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines The warning returned varies on whether the line itself is blank, for easier filtering for those who want to indent their blank lines. Okay: spam(1) W291: spam(1)\s W293: class Foo(object):\n \n bang = 12 """ physical_line = physical_line.rstrip('\n') # chr(10), newline physical_line = physical_line.rstrip('\r') # chr(13), carriage return physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L stripped = physical_line.rstrip() if physical_line != stripped: if stripped: return len(stripped), "W291 trailing whitespace" else: return 0, "W293 blank line contains whitespace" def trailing_blank_lines(physical_line, lines, line_number): r""" JCR: Trailing blank lines are superfluous. Okay: spam(1) W391: spam(1)\n """ if physical_line.strip() == '' and line_number == len(lines): return 0, "W391 blank line at end of file" def missing_newline(physical_line): """ JCR: The last line should have a newline. """ if physical_line.rstrip() == physical_line: return len(physical_line), "W292 no newline at end of file" def maximum_line_length(physical_line): """ Limit all lines to a maximum of 79 characters. There are still many devices around that are limited to 80 character lines; plus, limiting windows to 80 characters makes it possible to have several windows side-by-side. The default wrapping on such devices looks ugly. Therefore, please limit all lines to a maximum of 79 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended. """ line = physical_line.rstrip() length = len(line) if length > MAX_LINE_LENGTH: try: # The line could contain multi-byte characters if not hasattr(line, 'decode'): # Python 3 line = line.encode('latin-1') length = len(line.decode('utf-8')) except UnicodeDecodeError: pass if length > MAX_LINE_LENGTH: return MAX_LINE_LENGTH, "E501 line too long (%d characters)" % length ############################################################################## # Plugins (check functions) for logical lines ############################################################################## def blank_lines(logical_line, blank_lines, indent_level, line_number, previous_logical, previous_indent_level, blank_lines_before_comment): r""" Separate top-level function and class definitions with two blank lines. Method definitions inside a class are separated by a single blank line. Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations). Use blank lines in functions, sparingly, to indicate logical sections. Okay: def a():\n pass\n\n\ndef b():\n pass Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass E301: class Foo:\n b = 0\n def bar():\n pass E302: def a():\n pass\n\ndef b(n):\n pass E303: def a():\n pass\n\n\n\ndef b(n):\n pass E303: def a():\n\n\n\n pass E304: @decorator\n\ndef a():\n pass """ if line_number == 1: return # Don't expect blank lines before the first line max_blank_lines = max(blank_lines, blank_lines_before_comment) if previous_logical.startswith('@'): if max_blank_lines: return 0, "E304 blank lines found after function decorator" elif max_blank_lines > 2 or (indent_level and max_blank_lines == 2): return 0, "E303 too many blank lines (%d)" % max_blank_lines elif (logical_line.startswith('def ') or logical_line.startswith('class ') or logical_line.startswith('@')): if indent_level: if not (max_blank_lines or previous_indent_level < indent_level or DOCSTRING_REGEX.match(previous_logical)): return 0, "E301 expected 1 blank line, found 0" elif max_blank_lines != 2: return 0, "E302 expected 2 blank lines, found %d" % max_blank_lines def extraneous_whitespace(logical_line): """ Avoid extraneous whitespace in the following situations: - Immediately inside parentheses, brackets or braces. - Immediately before a comma, semicolon, or colon. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) E201: spam(ham[ 1], {eggs: 2}) E201: spam(ham[1], { eggs: 2}) E202: spam(ham[1], {eggs: 2} ) E202: spam(ham[1 ], {eggs: 2}) E202: spam(ham[1], {eggs: 2 }) E203: if x == 4: print x, y; x, y = y , x E203: if x == 4: print x, y ; x, y = y, x E203: if x == 4 : print x, y; x, y = y, x """ line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): text = match.group() char = text.strip() found = match.start() if text == char + ' ' and char in '([{': return found + 1, "E201 whitespace after '%s'" % char if text == ' ' + char and line[found - 1] != ',': if char in '}])': return found, "E202 whitespace before '%s'" % char if char in ',;:': return found, "E203 whitespace before '%s'" % char def missing_whitespace(logical_line): """ JCR: Each comma, semicolon or colon should be followed by whitespace. Okay: [a, b] Okay: (3,) Okay: a[1:4] Okay: a[:4] Okay: a[1:] Okay: a[1:4:2] E231: ['a','b'] E231: foo(bar,baz) """ line = logical_line for index in range(len(line) - 1): char = line[index] if char in ',;:' and line[index + 1] not in WHITESPACE: before = line[:index] if char == ':' and before.count('[') > before.count(']'): continue # Slice syntax, no space required if char == ',' and line[index + 1] == ')': continue # Allow tuple with only one element: (3,) return index, "E231 missing whitespace after '%s'" % char def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level): r""" Use 4 spaces per indentation level. For really old code that you don't want to mess up, you can continue to use 8-space tabs. Okay: a = 1 Okay: if a == 0:\n a = 1 E111: a = 1 Okay: for item in items:\n pass E112: for item in items:\npass Okay: a = 1\nb = 2 E113: a = 1\n b = 2 """ if indent_char == ' ' and indent_level % 4: return 0, "E111 indentation is not a multiple of four" indent_expect = previous_logical.endswith(':') if indent_expect and indent_level <= previous_indent_level: return 0, "E112 expected an indented block" if indent_level > previous_indent_level and not indent_expect: return 0, "E113 unexpected indentation" def whitespace_before_parameters(logical_line, tokens): """ Avoid extraneous whitespace in the following situations: - Immediately before the open parenthesis that starts the argument list of a function call. - Immediately before the open parenthesis that starts an indexing or slicing. Okay: spam(1) E211: spam (1) Okay: dict['key'] = list[index] E211: dict ['key'] = list[index] E211: dict['key'] = list [index] """ prev_type = tokens[0][0] prev_text = tokens[0][1] prev_end = tokens[0][3] for index in range(1, len(tokens)): token_type, text, start, end, line = tokens[index] if (token_type == tokenize.OP and text in '([' and start != prev_end and (prev_type == tokenize.NAME or prev_text in '}])') and # Syntax "class A (B):" is allowed, but avoid it (index < 2 or tokens[index - 2][1] != 'class') and # Allow "return (a.foo for a in range(5))" (not keyword.iskeyword(prev_text))): return prev_end, "E211 whitespace before '%s'" % text prev_type = token_type prev_text = text prev_end = end def whitespace_around_operator(logical_line): """ Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. Okay: a = 12 + 3 E221: a = 4 + 5 E222: a = 4 + 5 E223: a = 4\t+ 5 E224: a = 4 +\t5 """ for match in WHITESPACE_AROUND_OPERATOR_REGEX.finditer(logical_line): before, whitespace, after = match.groups() tab = whitespace == '\t' offset = match.start(2) if before in OPERATORS: return offset, (tab and "E224 tab after operator" or "E222 multiple spaces after operator") elif after in OPERATORS: return offset, (tab and "E223 tab before operator" or "E221 multiple spaces before operator") def missing_whitespace_around_operator(logical_line, tokens): r""" - Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not). - Use spaces around arithmetic operators. Okay: i = i + 1 Okay: submitted += 1 Okay: x = x * 2 - 1 Okay: hypot2 = x * x + y * y Okay: c = (a + b) * (a - b) Okay: foo(bar, key='word', *args, **kwargs) Okay: baz(**kwargs) Okay: negative = -1 Okay: spam(-1) Okay: alpha[:-i] Okay: if not -5 < x < +5:\n pass Okay: lambda *args, **kw: (args, kw) E225: i=i+1 E225: submitted +=1 E225: x = x*2 - 1 E225: hypot2 = x*x + y*y E225: c = (a+b) * (a-b) E225: c = alpha -4 E225: z = x **y """ parens = 0 need_space = False prev_type = tokenize.OP prev_text = prev_end = None for token_type, text, start, end, line in tokens: if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): # ERRORTOKEN is triggered by backticks in Python 3000 continue if text in ('(', 'lambda'): parens += 1 elif text == ')': parens -= 1 if need_space: if start != prev_end: need_space = False elif text == '>' and prev_text == '<': # Tolerate the "<>" operator, even if running Python 3 pass else: return prev_end, "E225 missing whitespace around operator" elif token_type == tokenize.OP and prev_end is not None: if text == '=' and parens: # Allow keyword args or defaults: foo(bar=None). pass elif text in BINARY_OPERATORS: need_space = True elif text in UNARY_OPERATORS: # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). if prev_type == tokenize.OP: if prev_text in '}])': need_space = True elif prev_type == tokenize.NAME: if prev_text not in E225NOT_KEYWORDS: need_space = True else: need_space = True if need_space and start == prev_end: return prev_end, "E225 missing whitespace around operator" prev_type = token_type prev_text = text prev_end = end def whitespace_around_comma(logical_line): """ Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. JCR: This should also be applied around comma etc. Note: these checks are disabled by default Okay: a = (1, 2) E241: a = (1, 2) E242: a = (1,\t2) """ line = logical_line for separator in ',;:': found = line.find(separator + ' ') if found > -1: return found + 1, "E241 multiple spaces after '%s'" % separator found = line.find(separator + '\t') if found > -1: return found + 1, "E242 tab after '%s'" % separator def whitespace_around_named_parameter_equals(logical_line): """ Don't use spaces around the '=' sign when used to indicate a keyword argument or a default parameter value. Okay: def complex(real, imag=0.0): Okay: return magic(r=real, i=imag) Okay: boolean(a == b) Okay: boolean(a != b) Okay: boolean(a <= b) Okay: boolean(a >= b) E251: def complex(real, imag = 0.0): E251: return magic(r = real, i = imag) """ parens = 0 for match in WHITESPACE_AROUND_NAMED_PARAMETER_REGEX.finditer( logical_line): text = match.group() if parens and len(text) == 3: issue = "E251 no spaces around keyword / parameter equals" return match.start(), issue if text == '(': parens += 1 elif text == ')': parens -= 1 def whitespace_before_inline_comment(logical_line, tokens): """ Separate inline comments by at least two spaces. An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space. Okay: x = x + 1 # Increment x Okay: x = x + 1 # Increment x E261: x = x + 1 # Increment x E262: x = x + 1 #Increment x E262: x = x + 1 # Increment x """ prev_end = (0, 0) for token_type, text, start, end, line in tokens: if token_type == tokenize.NL: continue if token_type == tokenize.COMMENT: if not line[:start[1]].strip(): continue if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: return (prev_end, "E261 at least two spaces before inline comment") if (len(text) > 1 and text.startswith('# ') or not text.startswith('# ')): return start, "E262 inline comment should start with '# '" else: prev_end = end def imports_on_separate_lines(logical_line): r""" Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os Okay: from subprocess import Popen, PIPE Okay: from myclas import MyClass Okay: from foo.bar.yourclass import YourClass Okay: import myclass Okay: import foo.bar.yourclass """ line = logical_line if line.startswith('import '): found = line.find(',') if found > -1: return found, "E401 multiple imports on one line" def compound_statements(logical_line): r""" Compound statements (multiple statements on the same line) are generally discouraged. While sometimes it's okay to put an if/for/while with a small body on the same line, never do this for multi-clause statements. Also avoid folding such long lines! Okay: if foo == 'blah':\n do_blah_thing() Okay: do_one() Okay: do_two() Okay: do_three() E701: if foo == 'blah': do_blah_thing() E701: for x in lst: total += x E701: while t < 10: t = delay() E701: if foo == 'blah': do_blah_thing() E701: else: do_non_blah_thing() E701: try: something() E701: finally: cleanup() E701: if foo == 'blah': one(); two(); three() E702: do_one(); do_two(); do_three() """ line = logical_line found = line.find(':') if -1 < found < len(line) - 1: before = line[:found] if (before.count('{') <= before.count('}') and # {'a': 1} (dict) before.count('[') <= before.count(']') and # [1:2] (slice) not re.search(r'\blambda\b', before)): # lambda x: x return found, "E701 multiple statements on one line (colon)" found = line.find(';') if -1 < found: return found, "E702 multiple statements on one line (semicolon)" def python_3000_has_key(logical_line): """ The {}.has_key() method will be removed in the future version of Python. Use the 'in' operation instead, like: d = {"a": 1, "b": 2} if "b" in d: print d["b"] """ pos = logical_line.find('.has_key(') if pos > -1: return pos, "W601 .has_key() is deprecated, use 'in'" def python_3000_raise_comma(logical_line): """ When raising an exception, use "raise ValueError('message')" instead of the older form "raise ValueError, 'message'". The paren-using form is preferred because when the exception arguments are long or include string formatting, you don't need to use line continuation characters thanks to the containing parentheses. The older form will be removed in Python 3000. """ match = RAISE_COMMA_REGEX.match(logical_line) if match: return match.start(1), "W602 deprecated form of raising exception" def python_3000_not_equal(logical_line): """ != can also be written <>, but this is an obsolete usage kept for backwards compatibility only. New code should always use !=. The older syntax is removed in Python 3000. """ pos = logical_line.find('<>') if pos > -1: return pos, "W603 '<>' is deprecated, use '!='" def python_3000_backticks(logical_line): """ Backticks are removed in Python 3000. Use repr() instead. """ pos = logical_line.find('`') if pos > -1: return pos, "W604 backticks are deprecated, use 'repr()'" ############################################################################## # Helper functions ############################################################################## if '' == ''.encode(): # Python 2: implicit encoding. def readlines(filename): return open(filename).readlines() else: # Python 3: decode to latin-1. # This function is lazy, it does not read the encoding declaration. # XXX: use tokenize.detect_encoding() def readlines(filename): return open(filename, encoding='latin-1').readlines() def expand_indent(line): """ Return the amount of indentation. Tabs are expanded to the next multiple of 8. >>> expand_indent(' ') 4 >>> expand_indent('\\t') 8 >>> expand_indent(' \\t') 8 >>> expand_indent(' \\t') 8 >>> expand_indent(' \\t') 16 """ result = 0 for char in line: if char == '\t': result = result // 8 * 8 + 8 elif char == ' ': result += 1 else: break return result def mute_string(text): """ Replace contents with 'xxx' to prevent syntax matching. >>> mute_string('"abc"') '"xxx"' >>> mute_string("'''abc'''") "'''xxx'''" >>> mute_string("r'abc'") "r'xxx'" """ start = 1 end = len(text) - 1 # String modifiers (e.g. u or r) if text.endswith('"'): start += text.index('"') elif text.endswith("'"): start += text.index("'") # Triple quotes if text.endswith('"""') or text.endswith("'''"): start += 2 end -= 2 return text[:start] + 'x' * (end - start) + text[end:] def message(text): """Print a message.""" # print >> sys.stderr, options.prog + ': ' + text # print >> sys.stderr, text print(text) ############################################################################## # Framework to run all checks ############################################################################## def find_checks(argument_name): """ Find all globally visible functions where the first argument name starts with argument_name. """ checks = [] for name, function in globals().items(): if not inspect.isfunction(function): continue args = inspect.getargspec(function)[0] if args and args[0].startswith(argument_name): codes = ERRORCODE_REGEX.findall(inspect.getdoc(function) or '') for code in codes or ['']: if not code or not ignore_code(code): checks.append((name, function, args)) break checks.sort() return checks class Checker(object): """ Load a Python source file, tokenize it, check coding style. """ def __init__(self, filename, lines=None): self.filename = filename if filename is None: self.filename = 'stdin' self.lines = lines or [] elif lines is None: self.lines = readlines(filename) else: self.lines = lines options.counters['physical lines'] += len(self.lines) def readline(self): """ Get the next line from the input buffer. """ self.line_number += 1 if self.line_number > len(self.lines): return '' return self.lines[self.line_number - 1] def readline_check_physical(self): """ Check and return the next physical line. This method can be used to feed tokenize.generate_tokens. """ line = self.readline() if line: self.check_physical(line) return line def run_check(self, check, argument_names): """ Run a check plugin. """ arguments = [] for name in argument_names: arguments.append(getattr(self, name)) return check(*arguments) def check_physical(self, line): """ Run all physical checks on a raw input line. """ self.physical_line = line if self.indent_char is None and len(line) and line[0] in ' \t': self.indent_char = line[0] for name, check, argument_names in options.physical_checks: result = self.run_check(check, argument_names) if result is not None: offset, text = result self.report_error(self.line_number, offset, text, check) def build_tokens_line(self): """ Build a logical line from tokens. """ self.mapping = [] logical = [] length = 0 previous = None for token in self.tokens: token_type, text = token[0:2] if token_type in SKIP_TOKENS: continue if token_type == tokenize.STRING: text = mute_string(text) if previous: end_line, end = previous[3] start_line, start = token[2] if end_line != start_line: # different row prev_text = self.lines[end_line - 1][end - 1] if prev_text == ',' or (prev_text not in '{[(' and text not in '}])'): logical.append(' ') length += 1 elif end != start: # different column fill = self.lines[end_line - 1][end:start] logical.append(fill) length += len(fill) self.mapping.append((length, token)) logical.append(text) length += len(text) previous = token self.logical_line = ''.join(logical) assert self.logical_line.lstrip() == self.logical_line assert self.logical_line.rstrip() == self.logical_line def check_logical(self): """ Build a line from tokens and run all logical checks on it. """ options.counters['logical lines'] += 1 self.build_tokens_line() first_line = self.lines[self.mapping[0][1][2][0] - 1] indent = first_line[:self.mapping[0][1][2][1]] self.previous_indent_level = self.indent_level self.indent_level = expand_indent(indent) if options.verbose >= 2: print(self.logical_line[:80].rstrip()) for name, check, argument_names in options.logical_checks: if options.verbose >= 4: print(' ' + name) result = self.run_check(check, argument_names) if result is not None: offset, text = result if isinstance(offset, tuple): original_number, original_offset = offset else: for token_offset, token in self.mapping: if offset >= token_offset: original_number = token[2][0] original_offset = (token[2][1] + offset - token_offset) self.report_error(original_number, original_offset, text, check) self.previous_logical = self.logical_line def check_all(self, expected=None, line_offset=0): """ Run all checks on the input file. """ self.expected = expected or () self.line_offset = line_offset self.line_number = 0 self.file_errors = 0 self.indent_char = None self.indent_level = 0 self.previous_logical = '' self.blank_lines = 0 self.blank_lines_before_comment = 0 self.tokens = [] parens = 0 for token in tokenize.generate_tokens(self.readline_check_physical): if options.verbose >= 3: if token[2][0] == token[3][0]: pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) else: pos = 'l.%s' % token[3][0] print('l.%s\t%s\t%s\t%r' % (token[2][0], pos, tokenize.tok_name[token[0]], token[1])) self.tokens.append(token) token_type, text = token[0:2] if token_type == tokenize.OP and text in '([{': parens += 1 if token_type == tokenize.OP and text in '}])': parens -= 1 if token_type == tokenize.NEWLINE and not parens: self.check_logical() self.blank_lines = 0 self.blank_lines_before_comment = 0 self.tokens = [] if token_type == tokenize.NL and not parens: if len(self.tokens) <= 1: # The physical line contains only this token. self.blank_lines += 1 self.tokens = [] if token_type == tokenize.COMMENT: source_line = token[4] token_start = token[2][1] if source_line[:token_start].strip() == '': self.blank_lines_before_comment = max(self.blank_lines, self.blank_lines_before_comment) self.blank_lines = 0 if text.endswith('\n') and not parens: # The comment also ends a physical line. This works around # Python < 2.6 behaviour, which does not generate NL after # a comment which is on a line by itself. self.tokens = [] return self.file_errors def report_error(self, line_number, offset, text, check): """ Report an error, according to options. """ code = text[:4] if ignore_code(code): return if options.quiet == 1 and not self.file_errors: message(self.filename) if code in options.counters: options.counters[code] += 1 else: options.counters[code] = 1 options.messages[code] = text[5:] if options.quiet or code in self.expected: # Don't care about expected errors or warnings return self.file_errors += 1 if options.counters[code] == 1 or options.repeat: message("%s:%s:%d: %s" % (self.filename, self.line_offset + line_number, offset + 1, text)) if options.show_source: line = self.lines[line_number - 1] message(line.rstrip()) message(' ' * offset + '^') if options.show_pep8: message(check.__doc__.lstrip('\n').rstrip()) def input_file(filename): """ Run all checks on a Python source file. """ if options.verbose: message('checking ' + filename) errors = Checker(filename).check_all() def input_dir(dirname, runner=None): """ Check all Python source files in this directory and all subdirectories. """ dirname = dirname.rstrip('/') if excluded(dirname): return if runner is None: runner = input_file for root, dirs, files in os.walk(dirname): if options.verbose: message('directory ' + root) options.counters['directories'] += 1 dirs.sort() for subdir in dirs: if excluded(subdir): dirs.remove(subdir) files.sort() for filename in files: if filename_match(filename) and not excluded(filename): options.counters['files'] += 1 runner(os.path.join(root, filename)) def excluded(filename): """ Check if options.exclude contains a pattern that matches filename. """ basename = os.path.basename(filename) for pattern in options.exclude: if fnmatch(basename, pattern): # print basename, 'excluded because it matches', pattern return True def filename_match(filename): """ Check if options.filename contains a pattern that matches filename. If options.filename is unspecified, this always returns True. """ if not options.filename: return True for pattern in options.filename: if fnmatch(filename, pattern): return True def ignore_code(code): """ Check if options.ignore contains a prefix of the error code. If options.select contains a prefix of the error code, do not ignore it. """ for select in options.select: if code.startswith(select): return False for ignore in options.ignore: if code.startswith(ignore): return True def reset_counters(): for key in list(options.counters.keys()): if key not in BENCHMARK_KEYS: del options.counters[key] options.messages = {} def get_error_statistics(): """Get error statistics.""" return get_statistics("E") def get_warning_statistics(): """Get warning statistics.""" return get_statistics("W") def get_statistics(prefix=''): """ Get statistics for message codes that start with the prefix. prefix='' matches all errors and warnings prefix='E' matches all errors prefix='W' matches all warnings prefix='E4' matches all errors that have to do with imports """ stats = [] keys = list(options.messages.keys()) keys.sort() for key in keys: if key.startswith(prefix): stats.append('%-7s %s %s' % (options.counters[key], key, options.messages[key])) return stats def get_count(prefix=''): """Return the total count of errors and warnings.""" keys = list(options.messages.keys()) count = 0 for key in keys: if key.startswith(prefix): count += options.counters[key] return count def print_statistics(prefix=''): """Print overall statistics (number of errors and warnings).""" for line in get_statistics(prefix): print(line) def print_benchmark(elapsed): """ Print benchmark numbers. """ print('%-7.2f %s' % (elapsed, 'seconds elapsed')) for key in BENCHMARK_KEYS: print('%-7d %s per second (%d total)' % ( options.counters[key] / elapsed, key, options.counters[key])) def run_tests(filename): """ Run all the tests from a file. A test file can provide many tests. Each test starts with a declaration. This declaration is a single line starting with '#:'. It declares codes of expected failures, separated by spaces or 'Okay' if no failure is expected. If the file does not contain such declaration, it should pass all tests. If the declaration is empty, following lines are not checked, until next declaration. Examples: * Only E224 and W701 are expected: #: E224 W701 * Following example is conform: #: Okay * Don't check these lines: #: """ lines = readlines(filename) + ['#:\n'] line_offset = 0 codes = ['Okay'] testcase = [] for index, line in enumerate(lines): if not line.startswith('#:'): if codes: # Collect the lines of the test case testcase.append(line) continue if codes and index > 0: label = '%s:%s:1' % (filename, line_offset + 1) codes = [c for c in codes if c != 'Okay'] # Run the checker errors = Checker(filename, testcase).check_all(codes, line_offset) # Check if the expected errors were found for code in codes: if not options.counters.get(code): errors += 1 message('%s: error %s not found' % (label, code)) if options.verbose and not errors: message('%s: passed (%s)' % (label, ' '.join(codes))) # Keep showing errors for multiple tests reset_counters() # output the real line numbers line_offset = index # configure the expected errors codes = line.split()[1:] # empty the test case buffer del testcase[:] def selftest(): """ Test all check functions with test cases in docstrings. """ count_passed = 0 count_failed = 0 checks = options.physical_checks + options.logical_checks for name, check, argument_names in checks: for line in check.__doc__.splitlines(): line = line.lstrip() match = SELFTEST_REGEX.match(line) if match is None: continue code, source = match.groups() checker = Checker(None) for part in source.split(r'\n'): part = part.replace(r'\t', '\t') part = part.replace(r'\s', ' ') checker.lines.append(part + '\n') options.quiet = 2 checker.check_all() error = None if code == 'Okay': if len(options.counters) > len(BENCHMARK_KEYS): codes = [key for key in options.counters.keys() if key not in BENCHMARK_KEYS] error = "incorrectly found %s" % ', '.join(codes) elif not options.counters.get(code): error = "failed to find %s" % code # Reset the counters reset_counters() if not error: count_passed += 1 else: count_failed += 1 if len(checker.lines) == 1: print("pep8.py: %s: %s" % (error, checker.lines[0].rstrip())) else: print("pep8.py: %s:" % error) for line in checker.lines: print(line.rstrip()) if options.verbose: print("%d passed and %d failed." % (count_passed, count_failed)) if count_failed: print("Test failed.") else: print("Test passed.") def process_options(arglist=None): """ Process options passed either via arglist or via command line args. """ global options, args parser = OptionParser(version=__version__, usage="%prog [options] input ...") parser.add_option('-v', '--verbose', default=0, action='count', help="print status messages, or debug with -vv") parser.add_option('-q', '--quiet', default=0, action='count', help="report only file names, or nothing with -qq") parser.add_option('-r', '--repeat', action='store_true', help="show all occurrences of the same error") parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, help="exclude files or directories which match these " "comma separated patterns (default: %s)" % DEFAULT_EXCLUDE) parser.add_option('--filename', metavar='patterns', default='*.py', help="when parsing directories, only check filenames " "matching these comma separated patterns (default: " "*.py)") parser.add_option('--select', metavar='errors', default='', help="select errors and warnings (e.g. E,W6)") parser.add_option('--ignore', metavar='errors', default='', help="skip errors and warnings (e.g. E4,W)") parser.add_option('--show-source', action='store_true', help="show source code for each error") parser.add_option('--show-pep8', action='store_true', help="show text of PEP 8 for each error") parser.add_option('--statistics', action='store_true', help="count errors and warnings") parser.add_option('--count', action='store_true', help="print total number of errors and warnings " "to standard error and set exit code to 1 if " "total is not null") parser.add_option('--benchmark', action='store_true', help="measure processing speed") parser.add_option('--testsuite', metavar='dir', help="run regression tests from dir") parser.add_option('--doctest', action='store_true', help="run doctest on myself") options, args = parser.parse_args(arglist) if options.testsuite: args.append(options.testsuite) if not args and not options.doctest: parser.error('input not specified') options.prog = os.path.basename(sys.argv[0]) options.exclude = options.exclude.split(',') for index in range(len(options.exclude)): options.exclude[index] = options.exclude[index].rstrip('/') if options.filename: options.filename = options.filename.split(',') if options.select: options.select = options.select.split(',') else: options.select = [] if options.ignore: options.ignore = options.ignore.split(',') elif options.select: # Ignore all checks which are not explicitly selected options.ignore = [''] elif options.testsuite or options.doctest: # For doctest and testsuite, all checks are required options.ignore = [] else: # The default choice: ignore controversial checks options.ignore = DEFAULT_IGNORE.split(',') options.physical_checks = find_checks('physical_line') options.logical_checks = find_checks('logical_line') options.counters = dict.fromkeys(BENCHMARK_KEYS, 0) options.messages = {} return options, args def _main(): """ Parse options and run checks on Python source. """ options, args = process_options() if options.doctest: import doctest doctest.testmod(verbose=options.verbose) selftest() if options.testsuite: runner = run_tests else: runner = input_file start_time = time.time() for path in args: if os.path.isdir(path): input_dir(path, runner=runner) elif not excluded(path): options.counters['files'] += 1 runner(path) elapsed = time.time() - start_time if options.statistics: print_statistics() if options.benchmark: print_benchmark(elapsed) count = get_count() if count: if options.count: sys.stderr.write(str(count) + '\n') sys.exit(1) if __name__ == '__main__': _main() pocketlint-0.5.31/pocketlint/contrib/cssccc.py0000644000175000017500000003600011660475007022254 0ustar curtiscurtis00000000000000''' This code is in the public domain. Check CSS code for some common coding conventions. The code must be in a valid CSS format. It is recommend to first parse it using cssutils. It is also recommend to check it with pocket-lint for things like trailing spaces or tab characters. If a comment is on the whole line, it will consume the whole line like it was not there. If a comment is inside a line it will only consume its own content. Bases on Stoyan Stefanov's http://www.phpied.com/css-coding-conventions/ '@media' rule is not supported. @media print { html { background: #fff; color: #000; } body { padding: 1in; border: 0.5pt solid #666; } } The following at-rules are supported: * keyword / text at-rules * @charset "ISO-8859-15"; * @import url(/css/screen.css) screen, projection; * @namespace foo "http://example.com/ns/foo"; * keybord / block rules * @page { block; } * @font-face { block; } TODO: * add warning for using px for fonts. * add Unicode support. * add AtRule checks * add support for TAB as a separator / identation. * add support for @media ''' from __future__ import with_statement __version__ = '0.1.1' import sys SELECTOR_SEPARATOR = ',' DECLARATION_SEPARATOR = ';' PROPERTY_SEPARATOR = ':' COMMENT_START = r'/*' COMMENT_END = r'*/' AT_TEXT_RULES = ['import', 'charset', 'namespace'] AT_BLOCK_RULES = ['page', 'font-face'] # If you want # selector, # selector2 # { # property: # } #IGNORED_MESSAGES = ['I013', 'I014'] # If you want # selector, # selector { # property: # } #IGNORED_MESSAGES = ['I005', 'I014'] # If you want # selector, # selector2 { # property: # } IGNORED_MESSAGES = ['I005', 'I006'] class CSSRule(object): '''A CSS rule.''' def check(self): '''Check the rule.''' raise AssertionError('Method not implemtned.') class CSSAtRule(object): '''A CSS @rule.''' type = object() def __init__(self, identifier, keyword, log, text=None, block=None): self.identifier = identifier self.keyword = keyword self.text = text self.block = block self.log = log def check(self): '''Check the rule.''' class CSSRuleSet(object): '''A CSS rule_set.''' type = object() def __init__(self, selector, declarations, log): self.selector = selector self.declarations = declarations self.log = log def __str__(self): return '%s{%s}' % (str(self.selector), str(self.declarations)) def __repr__(self): return '%d:%s{%s}' % ( self.selector.start_line, str(self.selector), str(self.declarations), ) def check(self): '''Check the rule set.''' self.checkSelector() self.checkDeclarations() def checkSelector(self): '''Check rule-set selector.''' start_line = self.selector.getStartLine() selectors = self.selector.text.split(SELECTOR_SEPARATOR) offset = 0 last_selector = selectors[-1] first_selector = selectors[0] rest_selectors = selectors[1:] if first_selector.startswith('\n\n\n'): self.log(start_line, 'I002', 'To many newlines before selectors.') elif first_selector.startswith('\n\n'): pass elif start_line > 2: self.log(start_line, 'I003', 'To few newlines before selectors.') else: pass for selector in rest_selectors: if not selector.startswith('\n'): self.log( start_line + offset, 'I004', 'Selector must be on a new line.') offset += selector.count('\n') if not last_selector.endswith('\n'): self.log( start_line + offset, 'I005', 'No newline after last selector.') if not (last_selector[-2] != ' ' and last_selector[-1] == (' ')): self.log( start_line + offset, 'I013', 'Last selector must be followed by " {".') def checkDeclarations(self): '''Check rule-set declarations.''' start_line = self.declarations.getStartLine() declarations = self.declarations.text.split(DECLARATION_SEPARATOR) offset = 0 # Check all declarations except last as this is the new line. first_declaration = True for declaration in declarations[:-1]: if not declaration.startswith('\n'): self.log( start_line + offset, 'I007', 'Each declarations should start on a new line.', ) elif (not declaration.startswith('\n ') or declaration[5] == ' '): self.log( start_line + offset, 'I008', 'Each declaration must be indented with 4 spaces.', ) parts = declaration.split(PROPERTY_SEPARATOR) if len(parts) != 2: self.log( start_line + offset, 'I009', 'Wrong separator on property: value pair.', ) else: prop, value = parts if prop.endswith(' '): self.log( start_line + offset, 'I010', 'Whitespace before ":".', ) if not (value.startswith(' ') or value.startswith('\n')): self.log( start_line + offset, 'I011', 'Missing whitespace after ":".', ) elif value.startswith(' '): self.log( start_line + offset, 'I012', 'Multiple whitespaces after ":".', ) if first_declaration: first_declaration = False else: offset += declaration.count('\n') last_declaration = declarations[-1] offset += last_declaration.count('\n') if last_declaration != '\n': self.log( start_line + offset, 'I006', 'Rule declarations should end with a single new line.', ) if last_declaration != '\n ': self.log( start_line + offset, 'I014', 'Rule declarations should end indented on a single new line.', ) class CSSStatementMember(object): '''A member of CSS statement.''' def __init__(self, start_line, start_character, text): self.start_line = start_line self.start_character = start_character self.text = text def getStartLine(self): '''Return the line number for first character in the statement and the number of new lines untilg the first character.''' index = 0 text = self.text try: character = text[index] while character == '\n': index += 1 character = text[index] except IndexError: # The end of string was reached without finding a statement. pass return self.start_line + index + 1 def __str__(self): return self.text def __repr__(self): return '%d:%d:{%s}' % ( self.start_line, self.start_character, self.text) class CSSCodingConventionChecker(object): '''CSS coding convention checker.''' icons = { 'E': 'error', 'I': 'info', } def __init__(self, text, logger=None): self._text = text.splitlines(True) self.line_number = 0 self.character_number = 0 if logger: self._logger = logger else: self._logger = self._defaultLog def log(self, line_number, code, message): '''Log the message with `code`.''' if code in IGNORED_MESSAGES: return icon = self.icons[code[0]] self._logger(line_number, code + ': ' + message, icon=icon) def check(self): '''Check all rules.''' for rule in self.getRules(): rule.check() def getRules(self): '''Generates the next CSS rule ignoring comments.''' while True: yield self.getNextRule() def getNextRule(self): '''Return the next parsed rule. Raise `StopIteration` if we are at the last rule. ''' if self._nextStatementIsAtRule(): text = None block = None keyword = self._parse('@') # TODO: user regex [ \t {] keyword_text = self._parse(' ') keyword_name = keyword_text.text keyword.text += '@' + keyword_name + ' ' if keyword_name.lower() in AT_TEXT_RULES: text = self._parse(';') elif keyword_name.lower() in AT_BLOCK_RULES: start = self._parse('{') keyword.text += start.text block = self._parse('}') else: self._parse(';') raise StopIteration return CSSAtRule( identifier=keyword_name, keyword=keyword, text=text, block=block, log=self.log) else: selector = self._parse('{') declarations = self._parse('}') return CSSRuleSet( selector=selector, declarations=declarations, log=self.log) def _defaultLog(self, line_number, message, icon='info'): '''Log the message to STDOUT.''' print ' %4s:%s' % (line_number, message) def _nextStatementIsAtRule(self): '''Return True if next statement in the buffer is an at-rule. Just look for open brackets and see if there is an @ before that braket. ''' search_buffer = [] line_counter = self.line_number current_line = self._text[line_counter][self.character_number:] while current_line.find('@') == -1: search_buffer.append(current_line) line_counter += 1 try: current_line = self._text[line_counter] except IndexError: return False text_buffer = ''.join(search_buffer) if text_buffer.find('{') == -1: return True else: return False def _parse(self, stop_character): '''Return the parsed text until stop_character.''' try: self._text[self.line_number][self.character_number] except IndexError: raise StopIteration result = [] start_line = self.line_number start_character = self.character_number comment_started = False while True: try: data = self._text[self.line_number][self.character_number:] except IndexError: break # Look for comment start/end. (comment_update, before_comment, after_comment, newline_consumed) = _check_comment(data) if comment_update is not None: comment_started = comment_update if comment_started: # We are inside a comment. # Add the data before the comment and go to next line. if before_comment is not None: result.append(before_comment) self.character_number = 0 self.line_number += 1 continue # If we have a comment, strip it from the data. # Remember the initial cursor position to know where to # continue. initial_position = data.find(stop_character) if before_comment is not None or after_comment is not None: if before_comment is None: before_comment = '' if after_comment is None: after_comment = '' data = before_comment + after_comment if initial_position == -1 or newline_consumed: # We are not at the end. # Go to next line and append the data. result.append(data) self.character_number = 0 self.line_number += 1 continue else: # Delimiter found. # Find it again in the text that now has no comments. # Append data until the delimiter. # Move cursor to next character and stop searching for it. new_position = data.find(stop_character) result.append(data[:new_position]) self.character_number += initial_position + 1 break return CSSStatementMember( start_line=start_line, start_character=start_character, text=''.join(result)) def _check_comment(data): '''Check the data for comment markers.''' comment_started = None before_comment = None after_comment = None newline_consumed = False comment_start = data.find(COMMENT_START) if comment_start != -1: comment_started = True before_comment = data[:comment_start] # Only use `None` to signal that there is no text before the comment. if before_comment == '': before_comment = None comment_end = data.find(COMMENT_END) if comment_end != -1: comment_started = False # Set comment end after the lenght of the actual comment end # marker. comment_end += len(COMMENT_END) if before_comment is None and data[comment_end] == '\n': # Consume the new line if it next to the comment end and # the comment in on the whole line. comment_end += 1 newline_consumed = True after_comment = data[comment_end:] return (comment_started, before_comment, after_comment, newline_consumed) def show_usage(): '''Print the command usage.''' print 'Usage: cssccc OPTIONS' print ' -h, --help\t\tShow this help.' print ' -v, --version\t\tShow version.' print ' -f FILE, --file=FILE\tCheck FILE' def read_file(filename): '''Return the content of filename.''' text = '' with open(filename, 'r') as f: text = f.read() return text if __name__ == '__main__': if len(sys.argv) < 2: show_usage() elif sys.argv[1] in ['-v', '--version']: print 'CSS Code Convention Checker %s' % (__version__) sys.exit(0) elif sys.argv[1] == '-f': text = read_file(sys.argv[2]) checker = CSSCodingConventionChecker(text) sys.exit(checker.check()) elif sys.argv[1] == '--file=': text = read_file(sys.argv[1][len('--file='):]) checker = CSSCodingConventionChecker(text) sys.exit(checker.check()) else: show_usage() pocketlint-0.5.31/pocketlint/__init__.py0000644000175000017500000000000011412421657021076 0ustar curtiscurtis00000000000000pocketlint-0.5.31/pocketlint/jsreporter.js0000644000175000017500000000514111633177700021537 0ustar curtiscurtis00000000000000// Copyright (C) 2009-2011 - Curtis Hovey // This software is licensed under the MIT license (see the file COPYING). // Run like: // jsreporter.js function get_seed() { // Define a common global object like seed. var argv = ['gjs', 'jsreporter.js']; var i; for (i = 0; i < ARGV.length; i++) { argv.push(ARGV[i]); } return { 'print': print, 'argv': argv }; } var Seed = Seed || get_seed(); jslint_path = Seed.argv[2].substring(0, Seed.argv[2].lastIndexOf('/')); imports.searchPath.push(jslint_path); var JSLINT = imports.fulljslint.JSLINT; function get_file_content(file_path) { // Return the content of the file. var Gio = imports.gi.Gio; var file = Gio.file_new_for_path(file_path); var istream = file.read(null); var dstream = new Gio.DataInputStream({base_stream: istream}); var content_and_count = dstream.read_upto("", -1, null); istream.close(null); dstream = null; return content_and_count[0]; } function report_implied_names() { // Report about implied global names. var implied_names = []; var prop; for (prop in JSLINT.implied) { if (JSLINT.implied.hasOwnProperty(prop)) { implied_names.push(prop); } } if (implied_names.length > 0) { implied_names.sort(); return '0::0::Implied globals:' + implied_names.join(', '); } return ''; } function report_lint_errors() { // Report about lint errors. var errors = []; var i; for (i = 0; i < JSLINT.errors.length; i++) { var error = JSLINT.errors[i]; if (error === null) { error = { 'line': -1, 'character': -1, 'reason': 'JSLINT had a fatal error.' }; } // Fix the line and character offset for editors. error.line += 1; error.character += 1; errors.push( [error.line, error.character, error.reason].join('::')); } return errors.join('\n'); } function lint_script() { // Lint the source and report errors. var script = get_file_content(Seed.argv[3]); var result = JSLINT(script); if (! result) { var issues = []; errors = report_lint_errors(); if (errors) { issues.push(errors); } implied = report_implied_names(); if (implied) { issues.push(implied); } Seed.print(issues.join('\n')); } } lint_script(); pocketlint-0.5.31/pocketlint/formatcheck.py0000755000175000017500000010007511745773456021665 0ustar curtiscurtis00000000000000#!/usr/bin/python # Copyright (C) 2009-2012 - Curtis Hovey # This software is licensed under the MIT license (see the file COPYING). """Check for syntax and style problems.""" from __future__ import with_statement __metaclass__ = type __all__ = [ 'Reporter', 'UniversalChecker', ] import _ast import htmlentitydefs import logging import mimetypes import os import re import subprocess import sys from optparse import OptionParser from StringIO import StringIO from tokenize import TokenError from xml.etree import ElementTree try: from xml.etree.ElementTree import ParseError ParseError != '# Supress redefintion warning.' except ImportError: # Python 2.6 and below. ParseError = object() from xml.parsers.expat import ErrorString, ExpatError from formatdoctest import DoctestReviewer import contrib.pep8 as pep8 from contrib.cssccc import CSSCodingConventionChecker from contrib.pyflakes.checker import Checker as PyFlakesChecker try: import cssutils HAS_CSSUTILS = True except ImportError: HAS_CSSUTILS = False def find_exec(names): """Return the name of a GI enabled JS interpreter.""" if os.name != 'posix': return None for name in names: js = subprocess.Popen( ['which', name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) js_exec, ignore = js.communicate() if js.returncode == 0: return js_exec.strip() JS = find_exec(['gjs', 'seed']) DEFAULT_MAX_LENGTH = 80 class PocketLintPyFlakesChecker(PyFlakesChecker): '''PocketLint checker for pyflakes. This is here to work around some of the pyflakes problems. ''' def __init__(self, tree, file_path='(none)', text=None): self.text = text if self.text: self.text = self.text.split('\n') super(PocketLintPyFlakesChecker, self).__init__( tree=tree, filename=file_path) @property def file_path(self): '''Alias for consistency with the rest of pocketlint.''' return self.filename def report(self, messageClass, *args, **kwargs): '''Filter some errors not used in our project.''' line_no = args[0] - 1 # Ignore explicit pyflakes:ignore requests. if self.text and self.text[line_no].find('pyflakes:ignore') >= 0: return self.messages.append(messageClass(self.file_path, *args, **kwargs)) def NAME(self, node): '''Locate name. Ignore WindowsErrors.''' if node.id == 'WindowsError': return return super(PocketLintPyFlakesChecker, self).NAME(node) class Reporter: """Common rules for checkers.""" CONSOLE = object() FILE_LINES = object() COLLECTOR = object() def __init__(self, report_type, treeview=None): self.report_type = report_type self.file_lines_view = treeview if self.file_lines_view is not None: self.treestore = self.file_lines_view.get_model() self.piter = None self._last_file_name = None self.call_count = 0 self.error_only = False self.messages = [] def __call__(self, line_no, message, icon=None, base_dir=None, file_name=None): """Report a message.""" if self.error_only and icon != 'error': return self.call_count += 1 args = (line_no, message, icon, base_dir, file_name) if self.report_type == self.FILE_LINES: self._message_file_lines(*args) elif self.report_type == self.COLLECTOR: self._message_collector(*args) else: self._message_console(*args) def _message_console(self, line_no, message, icon=None, base_dir=None, file_name=None): """Print the messages to the console.""" self._message_console_group(base_dir, file_name) print ' %4s: %s' % (line_no, message) def _message_console_group(self, base_dir, file_name): """Print the file name is it has not been seen yet.""" source = (base_dir, file_name) if file_name is not None and source != self._last_file_name: self._last_file_name = source print '%s' % os.path.join('./', base_dir, file_name) def _message_file_lines(self, line_no, message, icon=None, base_dir=None, file_name=None): """Display the messages in the file_lines_view.""" if self.piter is None: mime_type = 'gnome-mime-text' self.piter = self.treestore.append( None, (file_name, mime_type, 0, None, base_dir)) self.treestore.append( self.piter, (file_name, icon, line_no, message, base_dir)) def _message_collector(self, line_no, message, icon=None, base_dir=None, file_name=None): self._last_file_name = (base_dir, file_name) self.messages.append((line_no, message)) class Language: """Supported Language types.""" TEXT = object() PYTHON = object() DOCTEST = object() CSS = object() JAVASCRIPT = object() SH = object() XML = object() XSLT = object() HTML = object() ZPT = object() ZCML = object() DOCBOOK = object() LOG = object() SQL = object() RESTRUCTUREDTEXT = object() XML_LIKE = (XML, XSLT, HTML, ZPT, ZCML, DOCBOOK) mimetypes.add_type('application/x-zope-configuation', '.zcml') mimetypes.add_type('application/x-zope-page-template', '.pt') mimetypes.add_type('text/x-python-doctest', '.doctest') mimetypes.add_type('text/x-twisted-application', '.tac') mimetypes.add_type('text/x-log', '.log') mimetypes.add_type('text/x-rst', '.rst') mime_type_language = { 'text/x-python': PYTHON, 'text/x-twisted-application': PYTHON, 'text/x-python-doctest': DOCTEST, 'text/css': CSS, 'text/html': HTML, 'text/plain': TEXT, 'text/x-sql': SQL, 'text/x-log': LOG, 'text/x-rst': RESTRUCTUREDTEXT, 'application/javascript': JAVASCRIPT, 'application/xml': XML, 'application/x-sh': SH, 'application/x-zope-configuation': ZCML, 'application/x-zope-page-template': ZPT, } doctest_pattern = re.compile( r'^.*(doc|test|stories).*/.*\.(txt|doctest)$') @staticmethod def get_language(file_path): """Return the language for the source.""" # Doctests can easilly be mistyped, so it must be checked first. if Language.doctest_pattern.match(file_path): return Language.DOCTEST mime_type, encoding = mimetypes.guess_type(file_path) if mime_type is None: # This could be a very bad guess. return Language.TEXT elif mime_type in Language.mime_type_language: return Language.mime_type_language[mime_type] elif mime_type in Language.XML_LIKE: return Language.XML elif mime_type.endswith('+xml'): return Language.XML elif 'text/' in mime_type: return Language.TEXT else: return None @staticmethod def is_editable(file_path): """ Only search mime-types that are like sources can open. A fuzzy match of text/ or +xml is good, but some files types are unknown or described as application data. """ return Language.get_language(file_path) is not None class BaseChecker: """Common rules for checkers. The Decedent must provide self.file_name and self.base_dir """ def __init__(self, file_path, text, reporter=None, options=None): self.file_path = file_path self.base_dir = os.path.dirname(file_path) self.file_name = os.path.basename(file_path) self.text = text self.set_reporter(reporter=reporter) self.options = options def set_reporter(self, reporter=None): """Set the reporter for messages.""" if reporter is None: reporter = Reporter(Reporter.CONSOLE) self._reporter = reporter def message(self, line_no, message, icon=None, base_dir=None, file_name=None): """Report the message.""" if base_dir is None: base_dir = self.base_dir if file_name is None: file_name = self.file_name self._reporter( line_no, message, icon=icon, base_dir=base_dir, file_name=file_name) def check(self): """Check the content.""" raise NotImplementedError @property def check_length_filter(self): '''Default filter used by default for checking line length.''' if self.options: return self.options.max_line_length else: return DEFAULT_MAX_LENGTH class UniversalChecker(BaseChecker): """Check and reformat doctests.""" def __init__(self, file_path, text, language=None, reporter=None, options=None): self.file_path = file_path self.base_dir = os.path.dirname(file_path) self.file_name = os.path.basename(file_path) self.text = text self.set_reporter(reporter=reporter) self.language = language self.options = options self.file_lines_view = None def check(self): """Check the file syntax and style.""" if self.language is Language.PYTHON: checker_class = PythonChecker elif self.language is Language.DOCTEST: checker_class = DoctestReviewer elif self.language is Language.CSS: checker_class = CSSChecker elif self.language in Language.XML_LIKE: checker_class = XMLChecker elif self.language is Language.JAVASCRIPT: checker_class = JavascriptChecker elif self.language is Language.LOG: # Log files are not source, but they are often in source code # trees. pass else: checker_class = AnyTextChecker checker = checker_class( self.file_path, self.text, self._reporter, self.options) checker.check() class AnyTextMixin: """Common checks for many checkers.""" def check_conflicts(self, line_no, line): """Check that there are no merge conflict markers.""" if line.startswith('<' * 7) or line.startswith('>' * 7): self.message(line_no, 'File has conflicts.', icon='errror') def check_length(self, line_no, line): """Check the length of the line.""" max_length = self.check_length_filter if len(line) > max_length: self.message( line_no, 'Line exceeds %s characters.' % max_length, icon='info') def check_trailing_whitespace(self, line_no, line): """Check for the presence of trailing whitespace in the line.""" if line.endswith(' '): self.message( line_no, 'Line has trailing whitespace.', icon='info') def check_tab(self, line_no, line): """Check for the presence of tabs in the line.""" if '\t' in line: self.message( line_no, 'Line contains a tab character.', icon='info') class AnyTextChecker(BaseChecker, AnyTextMixin): """Verify the text of the document.""" def check(self): """Call each line_method for each line in text.""" for line_no, line in enumerate(self.text.splitlines()): line_no += 1 self.check_length(line_no, line) self.check_trailing_whitespace(line_no, line) self.check_conflicts(line_no, line) class SQLChecker(BaseChecker, AnyTextMixin): """Verify SQL style.""" def check(self): """Call each line_method for each line in text.""" # Consider http://code.google.com/p/python-sqlparse/ to verify # keywords and reformatting. for line_no, line in enumerate(self.text.splitlines()): line_no += 1 self.check_trailing_whitespace(line_no, line) self.check_tab(line_no, line) self.check_conflicts(line_no, line) class XMLChecker(BaseChecker, AnyTextMixin): """Check XML documents.""" xml_decl_pattern = re.compile(r'<\?xml .*?\?>') xhtml_doctype = ( u'') def check(self): """Check the syntax of the python code.""" if self.text == '': return parser = ElementTree.XMLParser() parser.entity.update(htmlentitydefs.entitydefs) offset = 0 try: # This is a sanity check to work with the true text.... text = unicode(self.text.decode('utf-8').encode('utf-8')) except UnicodeDecodeError: # ...but this fallback is okay since this check is about markup. text = self.text.decode('ascii', 'ignore').encode('utf-8') if text.find('') != -1: text = text.replace('', self.xhtml_doctype) try: ElementTree.parse(StringIO(text), parser) except (ExpatError, ParseError), error: if hasattr(error, 'code'): error_message = ErrorString(error.code) if hasattr(error, 'position') and error.position: error_lineno, error_charno = error.position error_lineno = error_lineno - offset elif error.lineno: # Python 2.6- error_lineno = error.lineno - offset else: error_lineno = 0 else: error_message, location = str(error).rsplit(':') error_lineno = int(location.split(',')[0].split()[1]) - offset self.message(error_lineno, error_message, icon='error') self.check_text() def check_text(self): for line_no, line in enumerate(self.text.splitlines()): line_no += 1 self.check_trailing_whitespace(line_no, line) self.check_conflicts(line_no, line) class CSSReporterHandler(logging.Handler): """A logging handler that uses the checker to report issues.""" error_pattern = re.compile( r'(?P[^(]+): \((?P[^,]+, [^,]+), (?P\d+).*') message_pattern = re.compile( r'(?P[^:]+:[^:]+): (?P.*) (?P0)$') def __init__(self, checker): logging.Handler.__init__(self, logging.INFO) self.checker = checker def handleError(self, record): pass def emit(self, record): if record.levelname == 'ERROR': icon = 'error' else: icon = 'info' matches = self.checker.message_pattern.search(record.getMessage()) if matches is None: matches = self.error_pattern.search(record.getMessage()) try: line_no = matches.group('lineno') message = "%s: %s" % ( matches.group('issue'), matches.group('text')) except AttributeError: line_no = 0 message = record.getMessage() self.checker.message(int(line_no), message, icon=icon) class CSSChecker(BaseChecker, AnyTextMixin): """Check XML documents.""" message_pattern = re.compile( r'[^ ]+ (?P.*) \[(?P\d+):\d+: (?P.+)\]') def check(self): """Check the syntax of the CSS code.""" if self.text == '': return self.check_cssutils() self.check_text() # CSS coding conventoins checks should go last since they rely # on previous checks. self.check_css_coding_conventions() def check_cssutils(self): """Check the CSS code by parsing it using CSSUtils module.""" if not HAS_CSSUTILS: return handler = CSSReporterHandler(self) log = logging.getLogger('pocket-lint') log.addHandler(handler) log.propagate = False parser = cssutils.CSSParser( log=log, loglevel=logging.INFO, raiseExceptions=False) parser.parseString(self.text) log.removeHandler(handler) def check_text(self): """Call each line_method for each line in text.""" for line_no, line in enumerate(self.text.splitlines()): line_no += 1 self.check_length(line_no, line) self.check_trailing_whitespace(line_no, line) self.check_conflicts(line_no, line) self.check_tab(line_no, line) def check_css_coding_conventions(self): """Check the input using CSS Coding Convention checker.""" CSSCodingConventionChecker(self.text, logger=self.message).check() class PythonChecker(BaseChecker, AnyTextMixin): """Check python source code.""" # This regex is taken from PEP 0263. encoding_pattern = re.compile("coding[:=]\s*([-\w.]+)") def __init__(self, file_path, text, reporter=None, options=None): super(PythonChecker, self).__init__( file_path, text, reporter, options) self.encoding = 'ascii' def check(self): """Check the syntax of the python code.""" if self.text == '': return self.check_text() self.check_flakes() self.check_pep8() def check_flakes(self): """Check compilation and syntax.""" try: tree = compile( self.text, self.file_path, "exec", _ast.PyCF_ONLY_AST) except (SyntaxError, IndentationError), exc: line_no = exc.lineno or 0 line = exc.text or '' explanation = 'Could not compile; %s' % exc.msg message = '%s: %s' % (explanation, line.strip()) self.message(line_no, message, icon='error') else: warnings = PocketLintPyFlakesChecker( tree, file_path=self.file_path, text=self.text) for warning in warnings.messages: dummy, line_no, message = str(warning).split(':') self.message(int(line_no), message.strip(), icon='error') def check_pep8(self): """Check style.""" try: # Monkey patch pep8 for direct access to the messages. original_report_error = pep8.Checker.report_error def pep8_report_error(ignore, line_no, offset, message, check): self.message(line_no, message, icon='info') pep8.Checker.report_error = pep8_report_error original_max_line_length = pep8.MAX_LINE_LENGTH pep8.MAX_LINE_LENGTH = self.check_length_filter pep8.process_options([self.file_path]) try: pep8.Checker(self.file_path).check_all() except TokenError, er: message, location = er.args self.message(location[0], message, icon='error') except IndentationError, er: message, location = er.args message = "%s: %s" % (message, location[3].strip()) self.message(location[1], message, icon='error') finally: pep8.MAX_LINE_LENGTH = original_max_line_length pep8.Checker.report_error = original_report_error def check_text(self): """Call each line_method for each line in text.""" for line_no, line in enumerate(self.text.splitlines()): line_no += 1 if line_no in (1, 2): match = self.encoding_pattern.search(line) if match: self.encoding = match.group(1).lower() self.check_pdb(line_no, line) self.check_conflicts(line_no, line) self.check_ascii(line_no, line) def check_pdb(self, line_no, line): """Check the length of the line.""" pdb_call = 'pdb.' + 'set_trace' if pdb_call in line: self.message( line_no, 'Line contains a call to pdb.', icon='error') @property def check_length_filter(self): # The pep8 lib counts from 0. if self.options: return self.options.max_line_length - 1 else: return pep8.MAX_LINE_LENGTH def check_ascii(self, line_no, line): """Check that the line is ascii.""" if self.encoding != 'ascii': return try: line.encode('ascii') except UnicodeEncodeError, error: self.message( line_no, 'Non-ascii characer at position %s.' % error.end, icon='error') class JavascriptChecker(BaseChecker, AnyTextMixin): """Check python source code.""" HERE = os.path.dirname(__file__) FULLJSLINT = os.path.join(HERE, 'contrib/fulljslint.js') JSREPORTER = os.path.join(HERE, 'jsreporter.js') def check(self): """Check the syntax of the javascript code.""" if JS is None or self.text == '': return args = [JS, self.JSREPORTER, self.FULLJSLINT, self.file_path] jslint = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) issues, errors = jslint.communicate() issues = issues.strip() if issues: for issue in issues.splitlines(): line_no, char_no_, message = issue.split('::') line_no = int(line_no) line_no -= 1 self.message(line_no, message, icon='error') self.check_text() def check_debugger(self, line_no, line): """Check the length of the line.""" debugger_call = 'debugger;' if debugger_call in line: self.message( line_no, 'Line contains a call to debugger.', icon='error') def check_text(self): """Call each line_method for each line in text.""" for line_no, line in enumerate(self.text.splitlines()): line_no += 1 self.check_debugger(line_no, line) self.check_length(line_no, line) self.check_trailing_whitespace(line_no, line) self.check_conflicts(line_no, line) self.check_tab(line_no, line) class ReStructuredTextChecker(BaseChecker, AnyTextMixin): """Check reStructuredText ource code.""" # Taken from rst documentation. delimiter_characters = [ '=', '-', '`', ':', '\'', '"', '~', '^', '_', '*', '+', '#', '<', '>', ] def __init__(self, file_path, text, reporter=None): super(ReStructuredTextChecker, self).__init__( file_path, text, reporter=reporter) self.lines = self.text.splitlines() def check(self): """Check the syntax of the reStructuredText code.""" self.check_lines() self.check_empty_last_line() def check_lines(self): """Call each line checker for each line in text.""" for line_no, line in enumerate(self.lines): line_no += 1 self.check_length(line_no, line) self.check_trailing_whitespace(line_no, line) self.check_tab(line_no, line) self.check_conflicts(line_no, line) if self.isTransition(line_no - 1): self.check_transition(line_no - 1) elif self.isSectionDelimiter(line_no - 1): self.check_section_delimiter(line_no - 1) else: pass def isTransition(self, line_number): '''Return True if the current line is a line transition.''' line = self.lines[line_number] if len(line) < 4: return False if len(self.lines) < 3: return False succesive_characters = ( line[0] == line[1] == line[2] == line[3] and line[0] in self.delimiter_characters) if not succesive_characters: return False emply_lines_bounded = ( self.lines[line_number - 1] == '' and self.lines[line_number + 1] == '') if not emply_lines_bounded: return False return True def check_transition(self, line_number): '''Transitions should be delimited by a single emtpy line.''' if (self.lines[line_number - 2] == '' or self.lines[line_number + 2] == ''): self.message( line_number + 1, 'Transition markers should be bounded by single empty lines.', icon='info', ) def isSectionDelimiter(self, line_number): '''Return true if the line is a section delimiter.''' if len(self.lines) < 3: return False if line_number >= len(self.lines): return False line = self.lines[line_number] if len(line) < 3: return False if (line[0] == line[1] == line[2] and line[0] in self.delimiter_characters): if ' ' in line: # We have a table header. return False else: return True return False def check_section_delimiter(self, line_number): """Checks for section delimiter. These checkes are designed for sections delimited by top and bottom markers. ======= <- top marker Section <- text_line ======= <- bottom marker If the section is delimted only by bottom marker, the section text is considered the top marker. Section <- top marker, text_line ======= <- bottom marker If the section has a custom anchor name: .. _link <- top marker ======= Section <- text_line ======= <- bottom marker or: .. _link <- top marker Section <- text_line ======= <- bottom marker If we have top and bottom markers, the check will be called twice ( for each marker). In this case we will skip the tests for bottom marker. """ human_line_number = line_number + 1 current_line = self.lines[line_number] # Skip test if we have both top and bottom markers and we are # at the bottom marker. if (line_number > 1 and current_line == self.lines[line_number - 2]): return if ((line_number + 2) < len(self.lines) and current_line == self.lines[line_number + 2]): # We have both top and bottom markers and we are currently at # the top marker. top_marker = line_number text_line = line_number + 1 bottom_marker = line_number + 2 else: # We only have bottom marker, and are at the bottom marker. top_marker = line_number - 1 text_line = line_number - 1 bottom_marker = line_number # In case we have a custom anchor, the top_marker is replaced by # the custom anchor. if self._sectionHasCustomAnchor(top_marker): top_marker = top_marker - 2 # Check underline length for bottom marker, # since top marker can be the same as text line. if len(self.lines[bottom_marker]) != len(self.lines[text_line]): self.message( human_line_number, 'Section marker has wrong length.', icon='error', ) if not self._haveGoodSpacingBeforeSection(top_marker): self.message( human_line_number, 'Section should be divided by 2 empty lines.', icon='info', ) if not self._haveGoodSpacingAfterSection(bottom_marker): self.message( human_line_number, 'Section title should be followed by 1 empty line.', icon='info', ) def _sectionHasCustomAnchor(self, top_marker): if (top_marker - 2) < 0: return False if self.lines[top_marker - 2].startswith('.. _'): return True return False def _haveGoodSpacingBeforeSection(self, top_marker): '''Return True if we have good spacing before the section.''' if top_marker > 0: if self.lines[top_marker - 1] != '': return False # If we are on the second line, there is no space for 2 empty lines # before. if top_marker == 1: return False if top_marker > 1: if self.lines[top_marker - 2] != '': return False if top_marker > 2: if self.lines[top_marker - 3] == '': return False return True def _haveGoodSpacingAfterSection(self, bottom_marker): '''Return True if we have good spacing after the section.''' lines_count = len(self.lines) if bottom_marker < lines_count - 1: if self.lines[bottom_marker + 1] != '': return False if bottom_marker < lines_count - 2: if self.lines[bottom_marker + 2] == '': # If the section is followed by 2 empty spaces and then # followed by a section delimiter, the section delimiter # rules will take priority if self.isSectionDelimiter(bottom_marker + 3): return True if self.isSectionDelimiter(bottom_marker + 4): return True return False return True def check_empty_last_line(self): """Chech the files ends with an emtpy line and not with double empty line. This will avoid merge conflicts. """ if len(self.lines) < 2: return if self.text[-1] != '\n' or self.text[-2:] == '\n\n': self.message( len(self.lines), 'File does not ends with an empty line.', icon='info', ) def get_option_parser(): """Return the option parser for this program.""" usage = "usage: %prog [options] file1 file2" parser = OptionParser(usage=usage) parser.add_option( "-v", "--verbose", action="store_true", dest="verbose", help="show errors and warngings.") parser.add_option( "-q", "--quiet", action="store_false", dest="verbose", help="Show errors only.") parser.add_option( "-f", "--format", dest="do_format", action="store_true", help="Reformat the doctest.") parser.add_option( "-i", "--interactive", dest="is_interactive", action="store_true", help="Approve each change.") parser.add_option( "-m", "--max-length", dest="max_line_length", type="int", help="Set the max line length (default %s)" % DEFAULT_MAX_LENGTH) parser.set_defaults( verbose=True, do_format=False, is_interactive=False, max_line_length=DEFAULT_MAX_LENGTH, ) return parser def check_sources(sources, options, reporter=None): if reporter is None: reporter = Reporter(Reporter.CONSOLE) reporter.call_count = 0 for source in sources: file_path = os.path.normpath(source) if os.path.isdir(source) or not Language.is_editable(source): continue language = Language.get_language(file_path) with open(file_path) as file_: text = file_.read() if language is Language.DOCTEST and options.do_format: formatter = DoctestReviewer(text, file_path, reporter) formatter.format_and_save(options.is_interactive) checker = UniversalChecker( file_path, text, language, reporter, options=options) checker.check() return reporter.call_count def main(argv=None): """Run the command line operations.""" if argv is None: argv = sys.argv parser = get_option_parser() (options, sources) = parser.parse_args(args=argv[1:]) # Handle standard args. if len(sources) == 0: parser.error("Expected file paths.") reporter = Reporter(Reporter.CONSOLE) reporter.error_only = not options.verbose return check_sources(sources, options, reporter) if __name__ == '__main__': sys.exit(main()) pocketlint-0.5.31/setup.py0000755000175000017500000000327111767132445016352 0ustar curtiscurtis00000000000000#!/usr/bin/python import subprocess from distutils.core import ( Command, setup, ) from distutils.command.sdist import sdist class SignedSDistCommand(sdist): """Sign the source archive with a detached signature.""" description = "Sign the source archive after it is generated." def run(self): sdist.run(self) gpg_args = [ 'gpg', '--armor', '--sign', '--detach-sig', self.archive_files[0]] gpg = subprocess.Popen( gpg_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) gpg.communicate() class Check(Command): description = "Run unit tests" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def get_command_name(self): pass def run(self): test_args = ['./test.py'] test = subprocess.Popen( test_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, errput = test.communicate() print errput setup( name="pocketlint", description="Pocket-lint a composite linter and style checker.", version="0.5.31", maintainer="Curtis C. Hovey", maintainer_email="sinzui.is@verizon.net", url="https://launchpad.net/pocket-lint", packages=[ 'pocketlint', 'pocketlint/contrib', 'pocketlint/contrib/pyflakes'], package_dir={ 'pocketlint': 'pocketlint', 'pocketlint/contrib': 'pocketlint/contrib'}, package_data={ 'pocketlint': ['jsreporter.js'], 'pocketlint/contrib': ['fulljslint.js'], }, scripts=['scripts/pocketlint'], cmdclass={ 'check': Check, 'signed_sdist': SignedSDistCommand, }, )