master b62b2ae8ee52 cached
87 files
1.1 MB
257.8k tokens
674 symbols
1 requests
Download .txt
Showing preview only (1,162K chars total). Download the full file or copy to clipboard to get everything.
Repository: SublimeLinter/SublimeLinter-for-ST2
Branch: master
Commit: b62b2ae8ee52
Files: 87
Total size: 1.1 MB

Directory structure:
gitextract__zefwhfl/

├── .codeintel/
│   └── config
├── .gitignore
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── LICENSE
├── Main.sublime-menu
├── README.md
├── SublimeLinter.py
├── SublimeLinter.sublime-settings
├── changelog.txt
├── gutter_mark_themes/
│   └── sublime-linter-gutter-markers.psd
├── messages/
│   ├── 1.5.1.txt
│   ├── 1.5.2.txt
│   ├── 1.5.3.txt
│   ├── 1.5.4.txt
│   ├── 1.5.5.txt
│   ├── 1.5.6.txt
│   ├── 1.5.7.txt
│   ├── 1.6.0.txt
│   ├── 1.6.1.txt
│   ├── 1.6.10.txt
│   ├── 1.6.11.txt
│   ├── 1.6.12.txt
│   ├── 1.6.13.txt
│   ├── 1.6.2.txt
│   ├── 1.6.3.txt
│   ├── 1.6.4.txt
│   ├── 1.6.5.txt
│   ├── 1.6.6.txt
│   ├── 1.6.7.txt
│   ├── 1.6.8.txt
│   ├── 1.6.9.txt
│   ├── 1.7.0.txt
│   ├── 1.7.1.txt
│   ├── 1.7.2.txt
│   ├── SublimeLinter3-update1.txt
│   ├── SublimeLinter3-update2.txt
│   ├── SublimeLinter3-update3.txt
│   ├── SublimeLinter3.txt
│   └── install.txt
├── messages.json
├── package_control.json
└── sublimelinter/
    ├── __init__.py
    ├── loader.py
    └── modules/
        ├── __init__.py
        ├── base_linter.py
        ├── c.py
        ├── c_cpplint.py
        ├── coffeescript.py
        ├── css.py
        ├── git_commit_message.py
        ├── haml.py
        ├── haskell.py
        ├── html.py
        ├── java.py
        ├── javascript.py
        ├── libs/
        │   ├── capp_lint.py
        │   ├── csslint/
        │   │   ├── csslint-node.js
        │   │   └── linter.js
        │   ├── jsengines/
        │   │   ├── jsc.js
        │   │   └── node.js
        │   ├── jshint/
        │   │   ├── jshint.js
        │   │   └── linter.js
        │   ├── jslint/
        │   │   ├── jslint.js
        │   │   └── linter.js
        │   ├── pep8.py
        │   └── pyflakes/
        │       ├── __init__.py
        │       ├── __main__.py
        │       ├── api.py
        │       ├── checker.py
        │       ├── messages.py
        │       └── reporter.py
        ├── lua.py
        ├── notes.py
        ├── objective-j.py
        ├── perl.py
        ├── php.py
        ├── puppet-lint.py
        ├── puppet.py
        ├── python.py
        ├── ruby-lint.py
        ├── ruby.py
        ├── squirrel.py
        ├── sublime_pylint.py
        └── xml.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .codeintel/config
================================================
{
	"Python": {
		"pythonExtraPaths": [
			"libs",
			"~/Applications/Sublime Text 2.app/Contents/MacOS",
			"/Applications/Sublime Text 2.app/Contents/MacOS"
		]
	}
}


================================================
FILE: .gitignore
================================================
.git
.hg
.svn

*.pyc
*.pyo

.DS_Store


================================================
FILE: Default (Linux).sublime-keymap
================================================
[
    { "keys": ["ctrl+alt+l"], "command": "sublimelinter", "args": {"action": "lint"} },
    { "keys": ["ctrl+alt+e"], "command": "find_next_lint_error" },
    { "keys": ["ctrl+alt+shift+e"], "command": "find_previous_lint_error" }
]


================================================
FILE: Default (OSX).sublime-keymap
================================================
[
    { "keys": ["ctrl+super+l"], "command": "sublimelinter", "args": {"action": "lint"} },
    { "keys": ["ctrl+super+e"], "command": "find_next_lint_error" },
    { "keys": ["ctrl+super+shift+e"], "command": "find_previous_lint_error" }
]


================================================
FILE: Default (Windows).sublime-keymap
================================================
[
    { "keys": ["ctrl+alt+l"], "command": "sublimelinter", "args": {"action": "lint"} },
    { "keys": ["ctrl+alt+e"], "command": "find_next_lint_error" },
    { "keys": ["ctrl+alt+shift+e"], "command": "find_previous_lint_error" }
]


================================================
FILE: Default.sublime-commands
================================================
[
    {
        "caption": "SublimeLinter: Lint Current File",
        "command": "sublimelinter_lint",
        "args": {"action": "lint"}
    },
    {
        "caption": "SublimeLinter: Show Error List",
        "command": "sublimelinter_show_errors",
        "args": {"action": "lint", "show_popup": true}
    },
    {
        "caption": "SublimeLinter: Background Linting",
        "command": "sublimelinter_lint",
        "args": {"action": "on"}
    },
    {
        "caption": "SublimeLinter: Load-Save Linting",
        "command": "sublimelinter_enable_load_save",
        "args": {"action": "load-save"}
    },
    {
        "caption": "SublimeLinter: Save-Only Linting",
        "command": "sublimelinter_enable_save_only",
        "args": {"action": "save-only"}
    },
    {
        "caption": "SublimeLinter: Disable Linting",
        "command": "sublimelinter_disable",
        "args": {"action": "off"}
    },
    {
        "caption": "SublimeLinter: Extract Annotations",
        "command": "sublimelinter_annotations",
        "args": {}
    },
    {
        "caption": "SublimeLinter: Reset",
        "command": "sublimelinter_lint",
        "args": {"action": "reset"}
    },
    {
        "caption": "Preferences: SublimeLinter Settings – Default",
        "command": "open_file", "args":
        {
            "file": "${packages}/SublimeLinter/SublimeLinter.sublime-settings"
        }
    },
    {
        "caption": "Preferences: SublimeLinter Settings – User",
        "command": "open_file", "args":
        {
            "file": "${packages}/User/SublimeLinter.sublime-settings"
        }
    }
]


================================================
FILE: LICENSE
================================================
Copyright Germán M. Bravo, Aparajita Fishman, Jacob Swartwood, and other
contributors.

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.


================================================
FILE: Main.sublime-menu
================================================
[
    {
        "caption": "Preferences",
        "mnemonic": "n",
        "id": "preferences",
        "children":
        [
            {
                "caption": "Package Settings",
                "mnemonic": "P",
                "id": "package-settings",
                "children":
                [
                    {
                        "caption": "SublimeLinter",
                        "children":
                        [
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/SublimeLinter/README.md"},
                                "caption": "README"
                            },
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/SublimeLinter/changelog.txt"},
                                "caption": "Change Log"
                            },
                            { "caption": "-" },
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/SublimeLinter/SublimeLinter.sublime-settings"},
                                "caption": "Settings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/User/SublimeLinter.sublime-settings"},
                                "caption": "Settings – User"
                            },
                            {
                                "command": "open_file_settings",
                                "caption": "Settings – Syntax Specific – User"
                            },
                            { "caption": "-" },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/SublimeLinter/Default (OSX).sublime-keymap",
                                    "platform": "OSX"
                                },
                                "caption": "Key Bindings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/SublimeLinter/Default (Linux).sublime-keymap",
                                    "platform": "Linux"
                                },
                                "caption": "Key Bindings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/SublimeLinter/Default (Windows).sublime-keymap",
                                    "platform": "Windows"
                                },
                                "caption": "Key Bindings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/User/Default (OSX).sublime-keymap",
                                    "platform": "OSX"
                                },
                                "caption": "Key Bindings – User"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/User/Default (Linux).sublime-keymap",
                                    "platform": "Linux"
                                },
                                "caption": "Key Bindings – User"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/User/Default (Windows).sublime-keymap",
                                    "platform": "Windows"
                                },
                                "caption": "Key Bindings – User"
                            },
                            { "caption": "-" }
                        ]
                    }
                ]
            }
        ]
    }
]


================================================
FILE: README.md
================================================
SublimeLinter
=============

## SublimeLinter 3 has landed!

SublimeLinter for Sublime Text 3 is [here](https://github.com/SublimeLinter/SublimeLinter3), and it’s soooooo much better than before! Install it from Package Control and enjoy!

Unless someone else comes forward, SublimeLinter for Sublime Text 2 will no longer be supported. I strongly encourage everyone to upgrade to Sublime Text 3 and SublimeLinter 3 — you’ll be glad you did! Take a look at the [extensive documentation](http://sublimelinter.readthedocs.org/) to see the great new features in SublimeLinter 3.

## Share the love!

I spent hundreds of hours writing and documenting SublimeLinter 3 to make it the best it can be — easy to use, easy to configure, easy to update, easy to extend. If you use SublimeLinter and feel it is making your coding life better and easier, please consider making a donation to help fund development and support. Thank you!

To donate: https://github.com/SublimeLinter/SublimeLinter3#share-the-love

Thank you for your support!

---

SublimeLinter v1.7 Overview
---------

SublimeLinter is a plugin that supports "lint" programs (known as "linters"). SublimeLinter highlights
lines of code the linter deems to contain (potential) errors. It also
supports highlighting special annotations (for example: TODO) so that they
can be quickly located.

SublimeLinter has built in linters for the following languages:

* C/C++ - lint via `cppcheck`
* CoffeeScript - lint via `coffee -s -l`
* CSS - lint via built-in [csslint](http://csslint.net)
* Git Commit Messages - lint via built-in module based on [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
* Haml - syntax check via `haml -c`
* HTML - lint via `tidy` (actually [tidy for HTML5](http://w3c.github.com/tidy-html5/))
* Java - lint via `javac -Xlint`
* JavaScript - lint via built in [jshint](http://jshint.org), [jslint](http://jslint.com), or the [closure linter (gjslint)](https://developers.google.com/closure/utilities/docs/linter_howto) (if installed)
* Lua - syntax check via `luac`
* Objective-J - lint via built-in [capp_lint](https://github.com/aparajita/capp_lint)
* Perl - lint via [Perl::Critic](http://perlcritic.com/) or syntax+deprecation check via `perl -c`
* PHP - syntax check via `php -l`
* Puppet - syntax check via `puppet parser validate` or `puppet-lint`
* Python - native, moderately-complete lint
* Ruby - syntax check via `ruby -wc`
* Squirrel - syntax check via `sq`
* XML - lint via `xmllint`

Quickstart
------------

* Install using [Package Control ST2 plugin](http://wbond.net/sublime_packages/package_control/installation).
* `SublimeLinter` runs in the background (by default), linting files for style and potential errors.
* Season to taste (edit configuration) by editing `Preferences->Package Settings->SublimeLinter->Settings - User`.
* Produce better code!


Installing
----------
**With the Package Control plugin:** The easiest way to install SublimeLinter is through Package Control, which can be found at this site: http://wbond.net/sublime_packages/package_control

Once you install Package Control, restart ST2 and bring up the Command Palette (`Command+Shift+P` on OS X, `Control+Shift+P` on Linux/Windows). Select "Package Control: Install Package", wait while Package Control fetches the latest package list, then select SublimeLinter when the list appears. The advantage of using this method is that Package Control will automatically keep SublimeLinter up to date with the latest version.

**Without Git:** Download the latest source from [GitHub](https://github.com/SublimeLinter/SublimeLinter) and copy the SublimeLinter folder to your Sublime Text "Packages" directory.

**With Git:** Clone the repository in your Sublime Text "Packages" directory:

    git clone https://github.com/SublimeLinter/SublimeLinter.git


The "Packages" directory is located at:

* OS X:

        ~/Library/Application Support/Sublime Text 2/Packages/

* Linux:

        ~/.config/sublime-text-2/Packages/

* Windows:

        %APPDATA%/Sublime Text 2/Packages/

### JavaScript-based linters
If you plan to edit files that use a JavaScript-based linter (JavaScript, CSS), your system
must have a JavaScript engine installed. Mac OS X comes with a preinstalled JavaScript engine called
JavaScriptCore, which is used if Node.js is not installed. On Windows, you **must** install the
JavaScript engine Node.js, which can be downloaded from [the Node.js site](http://nodejs.org/#download).

On Mac OS X, you **must** install Node.js if you plan to edit JavaScript or CSS files that
use non-ASCII characters in strings or comments, because JavaScriptCore is not Unicode-aware.

After installing Node.js, if the Node.js executable ("node" on Mac OS X, "node.exe" on Windows)
cannot be found by SublimeLinter, you may have to set the path to the executable in the
"sublimelinter\_executable\_map" setting. See the "Configuring" section below for info on
SublimeLinter settings.


Using
-----
SublimeLinter runs in one of three modes, which is determined by the "sublimelinter" user setting:

* **Background mode (the default)** - When the "sublimelinter" setting is true, linting is performed in the background as you modify a file (if the relevant linter supports it). If you like instant feedback, this is the best way to use SublimeLinter. If you want feedback, but not instantly, you can try another mode or set a minimum queue delay with the "sublimelinter_delay" setting, so that the linter will only run after a certain amount of idle time.
* **Load-save mode** - When the "sublimelinter" setting is "load-save", linting is performed only when a file is loaded and after saving. Errors are cleared as soon as the file is modified.
* **Save-only mode** - When the "sublimelinter" setting is "save-only", linting is performed only after a file is saved. Errors are cleared as soon as the file is modified.
* **On demand mode** - When the "sublimelinter" setting is false, linting is performed only when initiated by you. Use the `Control+Command+L` (OS X) or `Control+Alt+L` (Linux/Windows) key equivalent or the Command Palette to lint the current file. If the current file has no associated linter, the command will not be available.

Within a file whose language/syntax is supported by SublimeLinter, you can control SublimeLinter via the Command Palette (`Command+Shift+P` on OS X, `Control+Shift+P` on Linux/Windows). The available commands are:

* **SublimeLinter: Lint Current File** - Lints the current file, highlights any errors and displays how many errors were found.
* **SublimeLinter: Show Error List** - Lints the current file, highlights any errors and displays a quick panel with any errors that are found. Selecting an item from the quick panel jumps to that line.
* **SublimeLinter: Background Linting** - Enables background linting mode for the current view and lints it.
* **SublimeLinter: Disable Linting** - Disables linting mode for the current view and clears all lint errors.
* **SublimeLinter: Load-Save Linting** - Enables load-save linting mode for the current view and clears all lint errors.
* **SublimeLinter: Save-Only Linting** - Enables save-only linting mode for the current view and clears all lint errors.
* **SublimeLinter: Reset** - Clears all lint errors and sets the linting mode to the value in the SublimeLinter.sublime-settings file.

Depending on the file and the current state of background enabling, some of the commands will not be available.

When an error is highlighted by the linter, putting the cursor on the offending line will result in the error message being displayed on the status bar.

If you want to be shown a popup list of all errors whenever a file is saved, modify the user setting:

    "sublimelinter_popup_errors_on_save": true

If there are errors in the file, a quick panel will appear which shows the error message, line number and source code for each error. The starting location of all errors on the line are marked with "^". Selecting an error in the quick panel jumps directly to the location of the first error on that line.

While editing a file, you can quickly move to the next/previous lint error with the following key equivalents:

* **OS X**:

        next: Control+Command+E
        prev: Control+Command+Shift+E

* **Linux, Windows**:

        next: Control+Alt+E
        prev: Control+Alt+Shift+E

By default the search will wrap. You can turn wrapping off with the user setting:

    "sublimelinter_wrap_find": false

Please note: these key commands may conflict with other important cmds (such as generating the € character - this was discussed in issue [#182](https://github.com/SublimeLinter/SublimeLinter/issues/182)). If these controls are problematic, you may always adjust your settings by copying the defaults stored in `Preferences->Package Settings->SublimeLinter->Key Bindings - Default` into `Preferences->Key Bindings - User` and then modifying the values appropriately.

Configuring
-----------
There are a number of settings available to customize the behavior of SublimeLinter and its linters. For the latest information on what settings are available, select the menu item `Preferences->Package Settings->SublimeLinter->Settings - Default`.

Do **NOT** edit the default SublimeLinter settings. Your changes will be lost when SublimeLinter is updated. ALWAYS edit the user SublimeLinter settings by selecting `Preferences->Package Settings->SublimeLinter->Settings - User`. Note that individual settings you include in your user settings will _completely_ replace the corresponding default setting, so you must provide that setting in its entirety.

### Linter-specific notes
Following are notes specific to individual linters that you should be aware of:

* **C/C++** - The default C/C++ linter is [cppcheck](http://cppcheck.sourceforge.net/), however Google's [cpplint.py](http://google-styleguide.googlecode.com/svn/trunk/cpplint/) is also supported.

  To swap `cppcheck` out for `cpplint.py` you will need to adjust `sublimelinter_syntax_map` and possibly `sublimelinter_executable_map` also. First change the _linter language_ for `C` and `C++` to `c_cpplint` via `sublimelinter_syntax_map`. If `cpplint.py` is not on your system `PATH`, then add an entry for `c_cpplint` into `sublimelinter_executable_map` with the path to the file. As usual add these adjustments to the SublimeLinter **User Settings** file. An example:

      "sublimelinter_syntax_map":
      {
        "Python Django": "python",
        "Ruby on Rails": "ruby",
        "C++": "c_cpplint",
        "C": "c_cpplint"
      },
      "sublimelinter_executable_map":
      {
        "c_cpplint": "/Users/[my username]/Desktop/cpplint.py"
      }

* **CSS** - This linter runs [csslint](http://csslint.net). This linter requires a JavaScript engine (like Node.js) to be installed (see notes above for the JavaScript linters: "jshint" or "jslint").

  By default all CSSLint settings are turned on. You may customize CSSLint behavior with the "csslint_options" setting. Please select `Preferences->Package Settings->SublimeLinter->Settings - Default` for more information on turning off or adjusting severity of tests. For more information about options available to CSSLint, see https://github.com/stubbornella/csslint/wiki/Rules.

* **HTML** - This linter will not run unless you have a version of tidy with HTML5 support. To use this linter, please see: https://github.com/w3c/tidy-html5

* **Java** - Because it uses `javac` to do linting, each time you run the linter the entire dependency graph of the current file will be checked. Depending on the number of classes you import, this can be **extremely** slow. Also note that you **must** provide the `-sourcepath`, `-classpath`, `-Xlint` and `{filename}` arguments to `javac` in your per-project settings. See "Per-project settings" below for more information.

* **JavaScript** - If the "javascript_linter" setting is "jshint" or "jslint", this linter runs [jshint](http://jshint.org) (or [jslint](http://jslint.com) respectively) using Node.js. See "JavaScript-based linters" above for information on how to install Node.js.

  If the "javascript_linter" setting is "gjslint", this linter runs the [closure linter (gjslint)](https://developers.google.com/closure/utilities/docs/linter_howto). After installation, if gjslint cannot be found by SublimeLinter, you may have to set the path to gjslint in the "sublimelinter\_executable\_map" setting.

  You may want to modify the options passed to jshint, jslint, or gjslint. This can be done by using the **jshint_options**, **jslint_options**, or **gjslint_options** setting. Refer to the jshint.org site, the jslint.com site, or run `gjslint --help` for more information on the configuration options available.

  SublimeLinter supports `.jshintrc` files. If using JSHint, SublimeLinter will recursively search the directory tree (from the file location to the file-system root directory). This functionality is specified in the [JSHint README](https://github.com/jshint/node-jshint/#within-your-projects-directory-tree).

* **Perl** - Due to a vulnerability (issue [#77](https://github.com/SublimeLinter/SublimeLinter/issues/77)) with the Perl linter, Perl syntax checking is no longer enabled by default. The default linter for Perl has been replaced by Perl::Critic. The standard Perl syntax checker can still be invoked by switching the "perl_linter" setting to "perl".

* **Puppet** - Optional alternative linter using puppet-lint. Install with `gem install puppet-lint`. Add these adjustments to the SublimeLinter **User Settings** file. An example:
"sublimelinter_syntax_map": { "Puppet": "puppet-lint" }. Note this only lints on file save.

* **Ruby** - If you are using rvm or rbenv, you will probably have to specify the full path to the ruby you are using in the "sublimelinter_executable_map" setting. See "Configuring" below for more info.

### Per-project settings
SublimeLinter supports per-project/per-language settings. This is useful if a linter requires path configuration on a per-project basis. To edit your project settings, select the menu item `Project->Edit Project`. If there is no "settings" object at the top level, add one and then add a "SublimeLinter" sub-object, like this:

    {
        "folders":
        [
            {
                "path": "/Users/aparajita/Projects/foo/src"
            }
        ],
        "settings":
        {
            "SublimeLinter":
            {
            }
        }
    }

Within the "SublimeLinter" object, you can add a settings object for each language. The language name must match the language item in the linter's CONFIG object, which can be found in the linter's source file in the SublimeLinter/sublimelinter/modules folder. Each language can have two settings:

* "working_directory" - If present and a valid absolute directory path, the working directory is set to this path before the linter executes. This is useful if you are providing linter arguments that contain paths and you want to use working directory-relative paths instead of absolute paths.
* "lint_args" - If present, it must be a sequence of string arguments to pass to the linter. If your linter expects a filename as an argument, use the argument "{filename}" as a placeholder. If it expects stdin, use "-". Note that if you provide this item, you are responsible for passing **all** required arguments to the linter, as it will override default arguments.

For example, let's say we are editing a Java project and want to use the "java" linter, which requires a source path and class path. In addition, we want to ignore serialization errors. Our project settings might look like this:

    {
        "folders":
        [
            {
                "path": "/Users/aparajita/Projects/foo/src"
            }
        ],
        "settings":
        {
            "SublimeLinter":
            {
                "Java":
                {
                    "working_directory": "/Users/aparajita/Projects/foo",

                    "lint_args":
                    [
                        "-sourcepath", "src",
                        "-classpath", "libs/log4j-1.2.9.jar:libs/commons-logging-1.1.jar",
                        "-Xlint", "-Xlint:-serial",
                        "{filename}"
                    ]
                }
            }
        }
    }

The jshint follows convention set by node-jshint (though node is not required) and will attempt to locate the configuration file for you starting in pwd. (or "present working directory") If this does not yield a .jshintrc file, it will move one level up (..) the directory tree all the way up to the filesystem root. If a file is found, it stops immediately and uses that set of configuration instead of "jshint_options".

### Customizing colors
**IMPORTANT** - The theme style names have recently changed. The old and new color
names are:

    Old                     New
    ---------------------   -----------------------------
    sublimelinter.<type>    sublimelinter.outline.<type>
    invalid.<type>          sublimelinter.underline.<type>

Please change the names in your color themes accordingly.

There are three types of "errors" flagged by SublimeLinter: illegal,
violation, and warning. For each type, SublimeLinter will indicate the offending
line and the character position at which the error occurred on the line.

By default SublimeLinter will outline offending lines using the background color
of the "sublimelinter.outline.<type>" theme style, and underline the character position
using the background color of the "sublimelinter.underline.<type>" theme style, where <type>
is one of the three error types.

If these styles are not defined, the color will be black when there is a light
background color and black when there is a dark background color. You may
define a single "sublimelinter.outline" or "sublimelinter.underline" style to color all three types,
or define separate substyles for one or more types to color them differently.

If you want to make the offending lines glaringly obvious (perhaps for those
who tend to ignore lint errors), you can set the user setting:

    "sublimelinter_mark_style": "fill"

When this is set true, lines that have errors will be colored with the background
and foreground color of the "sublime.outline.<type>" theme style. Unless you have defined
those styles, this setting should be left as "outline".

You may want to disable drawing of outline boxes entirely. If so, change
using the user setting to:

    "sublimelinter_mark_style": "none"

You may also mark lines with errors by putting an "x" in the gutter with the user setting:

    "sublimelinter_gutter_marks": true

To customize the colors used for highlighting errors and user notes, add the following
to your theme (adapting the color to your liking):

    <dict>
        <key>name</key>
        <string>SublimeLinter Annotations</string>
        <key>scope</key>
        <string>sublimelinter.annotations</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#FFFFAA</string>
            <key>foreground</key>
            <string>#FFFFFF</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Error Outline</string>
        <key>scope</key>
        <string>sublimelinter.outline.illegal</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#FF4A52</string>
            <key>foreground</key>
            <string>#FFFFFF</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Error Underline</string>
        <key>scope</key>
        <string>sublimelinter.underline.illegal</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#FF0000</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Warning Outline</string>
        <key>scope</key>
        <string>sublimelinter.outline.warning</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#DF9400</string>
            <key>foreground</key>
            <string>#FFFFFF</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Warning Underline</string>
        <key>scope</key>
        <string>sublimelinter.underline.warning</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#FF0000</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Violation Outline</string>
        <key>scope</key>
        <string>sublimelinter.outline.violation</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#ffffff33</string>
            <key>foreground</key>
            <string>#FFFFFF</string>
        </dict>
    </dict>
    <dict>
        <key>name</key>
        <string>SublimeLinter Violation Underline</string>
        <key>scope</key>
        <string>sublimelinter.underline.violation</string>
        <key>settings</key>
        <dict>
            <key>background</key>
            <string>#FF0000</string>
        </dict>
    </dict>

Troubleshooting
---------------
If a linter does not seem to be working, you can check the ST2 console to see if it was enabled. When SublimeLinter is loaded, you will see messages in the console like this:

    Reloading plugin /Users/aparajita/Library/Application Support/Sublime Text 2/Packages/SublimeLinter/sublimelinter_plugin.py
    SublimeLinter: JavaScript loaded
    SublimeLinter: annotations loaded
    SublimeLinter: Objective-J loaded
    SublimeLinter: perl loaded
    SublimeLinter: php loaded
    SublimeLinter: python loaded
    SublimeLinter: ruby loaded
    SublimeLinter: pylint loaded

The first time a linter is asked to lint, it will check to see if it can be enabled. You will then see messages like this:

    SublimeLinter: JavaScript enabled (using JavaScriptCore)
    SublimeLinter: Ruby enabled (using "ruby" for executable)

Let's say the ruby linter is not working. If you look at the console, you may see a message like this:

    SublimeLinter: ruby disabled ("ruby" cannot be found)

This means that the ruby executable cannot be found on your system, which means it is not installed or not in your executable path.

Creating New Linters
--------------------
If you wish to create a new linter to support a new language, SublimeLinter makes it easy. Here are the steps involved:

* Create a new file in sublimelinter/modules. If your linter uses an external executable, you will probably want to copy perl.py. If your linter uses built in code, copy objective-j.py. The convention is to name the file the same as the language that will be linted.

* Configure the CONFIG dict in your module. See the comments in base\_linter.py for information on the values in that dict. You only need to set the values in your module that differ from the defaults in base\_linter.py, as your module's CONFIG is merged with the default. Note that if your linter uses an external executable that does not take stdin, setting 'input\_method' to INPUT\_METHOD\_TEMP\_FILE will allow interactive linting with that executable.

* If your linter uses built in code, override `built_in_check()` and return the errors found.

* Override `parse_errors()` and process the errors. If your linter overrides `built_in_check()`, `parse_errors()` will receive the result of that method. If your linter uses an external executable, `parse_errors()` receives the raw output of the executable, stripped of leading and trailing whitespace.

* If you linter is powered via JavaScript (eg. Node.js), there are few steps that will simplify the integration.

  Create a folder matching your linter name in the `SublimeLinter/sublimelinter/modules/lib` directory. This folder should include the linting library JS file (eg. jshint.js, csslint-Node.js) and a **linter.js** file. The **linter.js** file should `require()` the actual linter library file and export a `lint()` function. The `lint()` function should return a list of errors back to the python language handler file (via the `errors` parameter to the `parse_errors()` method).

  Although **linter.js** should follow the Node.js api, the linter may also be run via JavaScriptCore on OS X if Node.js is not installed. In the case where JavaScriptCore is used, require + export are shimmed to keep things consistent. However, it is important not to assume that a full Node.js api is available. If you must know what JS engine you are using, you may check for `USING_JSC` to be set as `true` when JavaScriptCore is used.

  For examples of using the JS engines, see **csslint**, **jslint**, and **jshint** in `SublimeLinter/sublimelinter/modules/libs` and the respective python code of **css.py** and **javascript.py** in `SublimeLinter/sublimelinter/modules`.


If your linter has more complex requirements, see the comments for CONFIG in base\_linter.py, and use the existing linters as guides.

Beta
----
The SublimeLinter Beta package is available via Package Control. The beta version is considered unstable and should only be used for testing the next release. If you like living on the edge, please report any bugs you find on the [SublimeLinter issues](https://github.com/SublimeLinter/SublimeLinter/issues) page.


================================================
FILE: SublimeLinter.py
================================================
from functools import partial
import os
import re
import sys
import time
import threading

import sublime
import sublime_plugin

from sublimelinter.loader import Loader
from sublimelinter.modules.base_linter import INPUT_METHOD_FILE

LINTERS = {}     # mapping of language name to linter module
QUEUE = {}       # views waiting to be processed by linter
ERRORS = {}      # error messages on given line obtained from linter; they are
                 # displayed in the status bar when cursor is on line with error
VIOLATIONS = {}  # violation messages, they are displayed in the status bar
WARNINGS = {}    # warning messages, they are displayed in the status bar
UNDERLINES = {}  # underline regions related to each lint message
TIMES = {}       # collects how long it took the linting to complete
MOD_LOAD = Loader(os.getcwdu(), LINTERS)  # utility to load (and reload
                 # if necessary) linter modules [useful when working on plugin]


# For snappier linting, different delays are used for different linting times:
# (linting time, delays)
DELAYS = (
    (50, (50, 100)),
    (100, (100, 300)),
    (200, (200, 500)),
    (400, (400, 1000)),
    (800, (800, 2000)),
    (1600, (1600, 3000)),
)

# Select one of the predefined gutter mark themes, the options are:
# "alpha", "bright", "dark", "hard" and "simple"
MARK_THEMES = ('alpha', 'bright', 'dark', 'hard', 'simple')
# The path to the built-in gutter mark themes
MARK_THEMES_PATH = os.path.join('..', 'SublimeLinter', 'gutter_mark_themes')
# The original theme for anyone interested the previous minimalist approach
ORIGINAL_MARK_THEME = {
    'violation': 'dot',
    'warning': 'dot',
    'illegal': 'circle'
}

# All available settings for SublimeLinter;
# only these are inherited from SublimeLinter.sublime-settings
ALL_SETTINGS = [
    'annotations',
    'csslint_options',
    'gjslint_ignore',
    'gjslint_options',
    'javascript_linter',
    'jshint_options',
    'jslint_options',
    'pep8',
    'pep8_ignore',
    'perl_linter',
    'pyflakes_ignore',
    'pyflakes_ignore_import_*',
    'sublimelinter',
    'sublimelinter_delay',
    'sublimelinter_disable',
    'sublimelinter_executable_map',
    'sublimelinter_fill_outlines',
    'sublimelinter_gutter_marks',
    'sublimelinter_gutter_marks_theme',
    'sublimelinter_mark_style',
    'sublimelinter_notes',
    'sublimelinter_objj_check_ascii',
    'sublimelinter_popup_errors_on_save',
    'sublimelinter_syntax_map',
    'sublimelinter_wrap_find',
]


WHITESPACE_RE = re.compile(r'\s+')


def get_delay(t, view):
    delay = 0

    for _t, d in DELAYS:
        if _t <= t:
            delay = d
        else:
            break

    delay = delay or DELAYS[0][1]

    # If the user specifies a delay greater than the built in delay,
    # figure they only want to see marks when idle.
    minDelay = int(view.settings().get('sublimelinter_delay', 0) * 1000)

    if minDelay > delay[1]:
        erase_lint_marks(view)

    return (minDelay, minDelay) if minDelay > delay[1] else delay


def last_selected_lineno(view):
    viewSel = view.sel()
    if not viewSel:
        return None
    return view.rowcol(viewSel[0].end())[0]


def update_statusbar(view):
    vid = view.id()
    lineno = last_selected_lineno(view)
    errors = []

    if lineno is not None:
        if vid in ERRORS and lineno in ERRORS[vid]:
            errors.extend(ERRORS[vid][lineno])

        if vid in VIOLATIONS and lineno in VIOLATIONS[vid]:
            errors.extend(VIOLATIONS[vid][lineno])

        if vid in WARNINGS and lineno in WARNINGS[vid]:
            errors.extend(WARNINGS[vid][lineno])

    if errors:
        view.set_status('Linter', '; '.join(errors))
    else:
        view.erase_status('Linter')


def run_once(linter, view, **kwargs):
    '''run a linter on a given view regardless of user setting'''
    if not linter:
        return

    vid = view.id()
    ERRORS[vid] = {}
    VIOLATIONS[vid] = {}
    WARNINGS[vid] = {}
    start = time.time()
    text = view.substr(sublime.Region(0, view.size())).encode('utf-8')
    lines, error_underlines, violation_underlines, warning_underlines, ERRORS[vid], VIOLATIONS[vid], WARNINGS[vid] = linter.run(view, text, (view.file_name() or '').encode('utf-8'))

    UNDERLINES[vid] = error_underlines[:]
    UNDERLINES[vid].extend(violation_underlines)
    UNDERLINES[vid].extend(warning_underlines)

    add_lint_marks(view, lines, error_underlines, violation_underlines, warning_underlines)

    if view.settings().get('sublimelinter_notes'):
        highlight_notes(view)

    update_statusbar(view)
    end = time.time()
    TIMES[vid] = (end - start) * 1000  # Keep how long it took to lint

    if kwargs.get('event', None) == 'on_post_save' and view.settings().get('sublimelinter_popup_errors_on_save'):
        popup_error_list(view)


def popup_error_list(view):
    vid = view.id()
    errors = ERRORS[vid].copy()

    for message_map in [VIOLATIONS[vid], WARNINGS[vid]]:
        for line, messages in message_map.items():
            if line in errors:
                errors[line].extend(messages)
            else:
                errors[line] = messages

    # Flatten the errors into a list
    error_list = []

    for line in sorted(errors.keys()):
        for index, message in enumerate(errors[line]):
            error_list.append({'line': line, 'message': message})

    panel_items = []

    for error in error_list:
        line_text = view.substr(view.full_line(view.text_point(error['line'], 0)))
        item = [error['message'], u'{0}: {1}'.format(error['line'] + 1, line_text.strip())]
        panel_items.append(item)

    def on_done(selected_item):
        if selected_item == -1:
            return

        selected = view.sel()
        selected.clear()

        error = error_list[selected_item]
        region_begin = view.text_point(error['line'], 0)

        # Go to the first non-whitespace character of the line
        line_text = view.substr(view.full_line(region_begin))
        match = WHITESPACE_RE.match(line_text)

        if (match):
            region_begin += len(match.group(0))

        selected.add(sublime.Region(region_begin, region_begin))
        # We have to force a move to update the cursor position
        view.run_command('move', {'by': 'characters', 'forward': True})
        view.run_command('move', {'by': 'characters', 'forward': False})
        view.show_at_center(region_begin)

    view.window().show_quick_panel(panel_items, on_done)


def add_lint_marks(view, lines, error_underlines, violation_underlines, warning_underlines):
    '''Adds lint marks to view.'''
    vid = view.id()
    erase_lint_marks(view)
    types = {'warning': warning_underlines, 'violation': violation_underlines, 'illegal': error_underlines}

    for type_name, underlines in types.items():
        if underlines:
            view.add_regions('lint-underline-' + type_name, underlines, 'sublimelinter.underline.' + type_name, sublime.DRAW_EMPTY_AS_OVERWRITE)

    if lines:
        outline_style = view.settings().get('sublimelinter_mark_style', 'outline')

        # This test is for the legacy "fill" setting; it will be removed
        # in a future version (likely v1.7).
        if view.settings().get('sublimelinter_fill_outlines', False):
            outline_style = 'fill'

        gutter_mark_enabled = True if view.settings().get('sublimelinter_gutter_marks', False) else False

        gutter_mark_theme = view.settings().get('sublimelinter_gutter_marks_theme', 'simple')

        outlines = {'warning': [], 'violation': [], 'illegal': []}

        for line in ERRORS[vid]:
            outlines['illegal'].append(view.full_line(view.text_point(line, 0)))

        for line in WARNINGS[vid]:
            outlines['warning'].append(view.full_line(view.text_point(line, 0)))

        for line in VIOLATIONS[vid]:
            outlines['violation'].append(view.full_line(view.text_point(line, 0)))

        for lint_type in outlines:
            if outlines[lint_type]:
                args = [
                    'lint-outlines-{0}'.format(lint_type),
                    outlines[lint_type],
                    'sublimelinter.outline.{0}'.format(lint_type)
                ]

                gutter_mark_image = ''

                if gutter_mark_enabled:
                    if gutter_mark_theme == 'original':
                        gutter_mark_image = ORIGINAL_MARK_THEME[lint_type]
                    elif gutter_mark_theme in MARK_THEMES:
                        gutter_mark_image = os.path.join(MARK_THEMES_PATH, gutter_mark_theme + '-' + lint_type)
                    else:
                        gutter_mark_image = gutter_mark_theme + '-' + lint_type

                args.append(gutter_mark_image)

                if outline_style == 'none':
                    args.append(sublime.HIDDEN)
                elif outline_style == 'fill':
                    pass  # outlines are filled by default
                else:
                    args.append(sublime.DRAW_OUTLINED)
                view.add_regions(*args)


def erase_lint_marks(view):
    '''erase all "lint" error marks from view'''
    view.erase_regions('lint-underline-illegal')
    view.erase_regions('lint-underline-violation')
    view.erase_regions('lint-underline-warning')
    view.erase_regions('lint-outlines-illegal')
    view.erase_regions('lint-outlines-violation')
    view.erase_regions('lint-outlines-warning')
    view.erase_regions('lint-annotations')


def get_lint_regions(view, reverse=False, coalesce=False):
    vid = view.id()
    underlines = UNDERLINES.get(vid, [])[:]

    if (coalesce):
        # Each of these regions is one character, so transform it into the character points
        points = sorted([region.begin() for region in underlines])

        # Now coalesce adjacent characters into a single region
        underlines = []
        last_point = -999

        for point in points:
            if point != last_point + 1:
                underlines.append(sublime.Region(point, point))
            else:
                region = underlines[-1]
                underlines[-1] = sublime.Region(region.begin(), point)

            last_point = point

    # Now get all outlines, which includes the entire line where underlines are
    outlines = view.get_regions('lint-outlines-illegal')
    outlines.extend(view.get_regions('lint-outlines-violation'))
    outlines.extend(view.get_regions('lint-outlines-warning'))
    outlines.extend(view.get_regions('lint-annotations'))

    # If an outline region contains an underline region, use only the underline
    regions = underlines

    for outline in outlines:
        contains_underlines = False

        for underline in underlines:
            if outline.contains(underline):
                contains_underlines = True
                break

        if not contains_underlines:
            regions.append(outline)

    return sorted(regions, key=lambda x: x.begin(), reverse=reverse)


def select_lint_region(view, region):
    selected = view.sel()
    selected.clear()

    # Find the first underline region within the region to select.
    # If there are none, put the cursor at the beginning of the line.
    underlineRegion = find_underline_within(view, region)

    if underlineRegion is None:
        underlineRegion = sublime.Region(region.begin(), region.begin())

    selected.add(underlineRegion)
    view.show(underlineRegion, True)


def find_underline_within(view, region):
    underlines = view.get_regions('lint-underline-illegal')
    underlines.extend(view.get_regions('lint-underline-violation'))
    underlines.extend(view.get_regions('lint-underline-warning'))
    underlines.sort(key=lambda x: x.begin())

    for underline in underlines:
        if region.contains(underline):
            return underline

    return None


def syntax_name(view):
    syntax = os.path.basename(view.settings().get('syntax'))
    syntax = os.path.splitext(syntax)[0]
    return syntax


def select_linter(view, ignore_disabled=False):
    '''selects the appropriate linter to use based on language in current view'''
    syntax = syntax_name(view)
    lc_syntax = syntax.lower()
    language = None
    linter = None
    syntaxMap = view.settings().get('sublimelinter_syntax_map', {})

    if syntax in syntaxMap:
        language = syntaxMap.get(syntax, '').lower()
    elif lc_syntax in syntaxMap:
        language = syntaxMap.get(lc_syntax, '').lower()
    elif lc_syntax in LINTERS:
        language = lc_syntax

    if language:
        if ignore_disabled:
            disabled = []
        else:
            disabled = view.settings().get('sublimelinter_disable', [])

        if language not in disabled:
            linter = LINTERS.get(language)

            # If the enabled state is False, it must be checked.
            # Enabled checking has to be deferred to first view use because
            # user settings cannot be loaded during plugin startup.
            if linter is not None and not linter.enabled:
                enabled, message = linter.check_enabled(view)
                print 'SublimeLinter: {0} {1} ({2})'.format(language, 'enabled' if enabled else 'disabled', message)

                if not enabled:
                    del LINTERS['' + language]
                    linter = None

    return linter


def highlight_notes(view):
    '''highlight user-specified annotations in a file'''
    view.erase_regions('lint-annotations')
    text = view.substr(sublime.Region(0, view.size()))
    regions = LINTERS['annotations'].built_in_check(view, text, '')

    if regions:
        view.add_regions('lint-annotations', regions, 'sublimelinter.annotations', sublime.DRAW_EMPTY_AS_OVERWRITE)


def _update_view(view, filename, **kwargs):
    # It is possible that by the time the queue is run,
    # the original file is no longer being displayed in the view,
    # or the view may be gone. This happens especially when
    # viewing files temporarily by single-clicking on a filename
    # in the sidebar or when selecting a file through the choose file palette.
    valid_view = False
    view_id = view.id()

    for window in sublime.windows():
        for v in window.views():
            if v.id() == view_id:
                valid_view = True
                break

    if not valid_view or view.is_loading() or (view.file_name() or '').encode('utf-8') != filename:
        return

    try:
        run_once(select_linter(view), view, **kwargs)
    except RuntimeError, ex:
        print ex


def queue_linter(linter, view, timeout=-1, preemptive=False, event=None):
    '''Put the current view in a queue to be examined by a linter'''
    if linter is None:
        erase_lint_marks(view)  # may have changed file type and left marks behind

        # No point in queuing anything if no linters will run
        if not view.settings().get('sublimelinter_notes'):
            return

    if preemptive:
        timeout = busy_timeout = 0
    elif timeout == -1:
        timeout, busy_timeout = get_delay(TIMES.get(view.id(), 100), view)
    else:
        busy_timeout = timeout

    kwargs = {'timeout': timeout, 'busy_timeout': busy_timeout, 'preemptive': preemptive, 'event': event}
    queue(view, partial(_update_view, view, (view.file_name() or '').encode('utf-8'), **kwargs), kwargs)


def _callback(view, filename, kwargs):
    kwargs['callback'](view, filename, **kwargs)


def background_linter():
    __lock_.acquire()

    try:
        callbacks = QUEUE.values()
        QUEUE.clear()
    finally:
        __lock_.release()

    for callback in callbacks:
        sublime.set_timeout(callback, 0)

################################################################################
# Queue dispatcher system:

queue_dispatcher = background_linter
queue_thread_name = 'background linter'
MAX_DELAY = 10


def queue_loop():
    '''An infinite loop running the linter in a background thread meant to
       update the view after user modifies it and then does no further
       modifications for some time as to not slow down the UI with linting.'''
    global __signaled_, __signaled_first_

    while __loop_:
        #print 'acquire...'
        __semaphore_.acquire()
        __signaled_first_ = 0
        __signaled_ = 0
        #print 'DISPATCHING!', len(QUEUE)
        queue_dispatcher()


def queue(view, callback, kwargs):
    global __signaled_, __signaled_first_
    now = time.time()
    __lock_.acquire()

    try:
        QUEUE[view.id()] = callback
        timeout = kwargs['timeout']
        busy_timeout = kwargs['busy_timeout']

        if now < __signaled_ + timeout * 4:
            timeout = busy_timeout or timeout

        __signaled_ = now
        _delay_queue(timeout, kwargs['preemptive'])

        if not __signaled_first_:
            __signaled_first_ = __signaled_
            #print 'first',
        #print 'queued in', (__signaled_ - now)
    finally:
        __lock_.release()


def _delay_queue(timeout, preemptive):
    global __signaled_, __queued_
    now = time.time()

    if not preemptive and now <= __queued_ + 0.01:
        return  # never delay queues too fast (except preemptively)

    __queued_ = now
    _timeout = float(timeout) / 1000

    if __signaled_first_:
        if MAX_DELAY > 0 and now - __signaled_first_ + _timeout > MAX_DELAY:
            _timeout -= now - __signaled_first_
            if _timeout < 0:
                _timeout = 0
            timeout = int(round(_timeout * 1000, 0))

    new__signaled_ = now + _timeout - 0.01

    if __signaled_ >= now - 0.01 and (preemptive or new__signaled_ >= __signaled_ - 0.01):
        __signaled_ = new__signaled_
        #print 'delayed to', (preemptive, __signaled_ - now)

        def _signal():
            if time.time() < __signaled_:
                return
            __semaphore_.release()

        sublime.set_timeout(_signal, timeout)


def delay_queue(timeout):
    __lock_.acquire()
    try:
        _delay_queue(timeout, False)
    finally:
        __lock_.release()


# only start the thread once - otherwise the plugin will get laggy
# when saving it often.
__semaphore_ = threading.Semaphore(0)
__lock_ = threading.Lock()
__queued_ = 0
__signaled_ = 0
__signaled_first_ = 0

# First finalize old standing threads:
__loop_ = False
__pre_initialized_ = False


def queue_finalize(timeout=None):
    global __pre_initialized_

    for thread in threading.enumerate():
        if thread.isAlive() and thread.name == queue_thread_name:
            __pre_initialized_ = True
            thread.__semaphore_.release()
            thread.join(timeout)
queue_finalize()

# Initialize background thread:
__loop_ = True
__active_linter_thread = threading.Thread(target=queue_loop, name=queue_thread_name)
__active_linter_thread.__semaphore_ = __semaphore_
__active_linter_thread.start()

################################################################################

UNRECOGNIZED = '''
* Unrecognized option * :  %s
==============================================

'''


def view_in_tab(view, title, text, file_type):
    '''Helper function to display information in a tab.
    '''
    tab = view.window().new_file()
    tab.set_name(title)
    _id = tab.buffer_id()
    tab.set_scratch(_id)
    tab.settings().set('gutter', True)
    tab.settings().set('line_numbers', False)
    tab.set_syntax_file(file_type)
    ed = tab.begin_edit()
    tab.insert(ed, 0, text)
    tab.end_edit(ed)
    return tab, _id


def lint_views(linter):
    if not linter:
        return

    viewsToLint = []

    for window in sublime.windows():
        for view in window.views():
            viewLinter = select_linter(view)

            if viewLinter == linter:
                viewsToLint.append(view)

    for view in viewsToLint:
        queue_linter(linter, view, preemptive=True)


def reload_view_module(view):
    for name, linter in LINTERS.items():
        module = sys.modules[linter.__module__]

        if module.__file__.encode('utf-8') == (view.file_name() or '').encode('utf-8'):
            print 'SublimeLinter: reloading language:', linter.language
            MOD_LOAD.reload_module(module)
            lint_views(linter)
            break


def settings_changed():
    for window in sublime.windows():
        for view in window.views():
            linter = select_linter(view)

            if (linter):
                reload_settings(view)


def reload_settings(view):
    '''Restores user settings.'''
    settings = sublime.load_settings(__name__ + '.sublime-settings')
    settings.clear_on_change(__name__)
    settings.add_on_change(__name__, settings_changed)

    for setting in ALL_SETTINGS:
        if settings.get(setting) != None:
            view.settings().set(setting, settings.get(setting))

    if view.settings().get('sublimelinter') == None:
        view.settings().set('sublimelinter', True)


class LintCommand(sublime_plugin.TextCommand):
    '''command to interact with linters'''

    def __init__(self, view):
        self.view = view
        self.help_called = False

    def run_(self, action):
        '''method called by default via view.run_command;
           used to dispatch to appropriate method'''
        if not action:
            return

        try:
            lc_action = action.lower()
        except AttributeError:
            return

        if lc_action == 'reset':
            self.reset()
        elif lc_action == 'on':
            self.on()
        elif lc_action == 'load-save':
            self.enable_load_save()
        elif lc_action == 'save-only':
            self.enable_save_only()
        elif lc_action == 'off':
            self.off()
        elif action.lower() in LINTERS:
            self._run(lc_action)

    def reset(self):
        '''Removes existing lint marks and restores user settings.'''
        erase_lint_marks(self.view)
        reload_settings(self.view)

    def on(self):
        '''Turns background linting on.'''
        self.view.settings().set('sublimelinter', True)
        queue_linter(select_linter(self.view), self.view, preemptive=True)

    def enable_load_save(self):
        '''Turns load-save linting on.'''
        self.view.settings().set('sublimelinter', 'load-save')
        erase_lint_marks(self.view)

    def enable_save_only(self):
        '''Turns save-only linting on.'''
        self.view.settings().set('sublimelinter', 'save-only')
        erase_lint_marks(self.view)

    def off(self):
        '''Turns background linting off.'''
        self.view.settings().set('sublimelinter', False)
        erase_lint_marks(self.view)

    def _run(self, name):
        '''runs an existing linter'''
        run_once(LINTERS[name.lower()], self.view)


class BackgroundLinter(sublime_plugin.EventListener):
    '''This plugin controls a linter meant to work in the background
    to provide interactive feedback as a file is edited. It can be
    turned off via a setting.
    '''

    def __init__(self):
        super(BackgroundLinter, self).__init__()
        self.lastSelectedLineNo = -1

    def on_modified(self, view):
        if view.is_scratch():
            return

        if view.settings().get('sublimelinter') != True:
            erase_lint_marks(view)
            return

        linter = select_linter(view)

        # File-based linters are not invoked during a modify
        if linter and linter.input_method == INPUT_METHOD_FILE:
            erase_lint_marks(view)
            return

        # Reset the last selected line number so that the current line will show error messages
        # when update_statusbar is called.
        self.lastSelectedLineNo = -1
        queue_linter(linter, view)

    def on_load(self, view):
        reload_settings(view)

        sublimelinter_setting = view.settings().get('sublimelinter')

        if view.is_scratch() or sublimelinter_setting == False or sublimelinter_setting == 'save-only':
            return

        queue_linter(select_linter(view), view, event='on_load')

    def on_post_save(self, view):
        sublimelinter_setting = view.settings().get('sublimelinter')

        if sublimelinter_setting == None:
            reload_settings(view)

        if view.is_scratch() or sublimelinter_setting == False:
            return

        reload_view_module(view)
        queue_linter(select_linter(view), view, preemptive=True, event='on_post_save')

    def on_selection_modified(self, view):
        if view.is_scratch():
            return
        delay_queue(1000)  # on movement, delay queue (to make movement responsive)

        # We only display errors in the status bar for the last line in the current selection.
        # If that line number has not changed, there is no point in updating the status bar.
        lastSelectedLineNo = last_selected_lineno(view)

        if lastSelectedLineNo != self.lastSelectedLineNo:
            self.lastSelectedLineNo = lastSelectedLineNo
            update_statusbar(view)


class FindLintErrorCommand(sublime_plugin.TextCommand):
    '''This command is just a superclass for other commands, it is never enabled.'''
    def is_enabled(self):
        return select_linter(self.view) is not None

    def find_lint_error(self, forward):
        linter = select_linter(self.view, ignore_disabled=True)

        if not linter:
            return

        self.view.run_command('lint', linter.language)
        regions = get_lint_regions(self.view, reverse=not forward, coalesce=True)

        if len(regions) == 0:
            sublime.error_message('No lint errors.')
            return

        selected = self.view.sel()
        point = selected[0].begin() if forward else selected[-1].end()
        regionToSelect = None

        # If going forward, find the first region beginning after the point.
        # If going backward, find the first region ending before the point.
        # If nothing is found in the given direction, wrap to the first/last region.
        if forward:
            for index, region in enumerate(regions):
                if point < region.begin():
                    regionToSelect = region
                    break
        else:
            for index, region in enumerate(regions):
                if point > region.end():
                    regionToSelect = region
                    break

        # If there is only one error line and the cursor is in that line, we cannot move.
        # Otherwise wrap to the first/last error line unless settings disallow that.
        if regionToSelect is None and (len(regions) > 1 or not regions[0].contains(point)):
            if self.view.settings().get('sublimelinter_wrap_find', True):
                regionToSelect = regions[0]

        if regionToSelect is not None:
            select_lint_region(self.view, regionToSelect)
        else:
            sublime.error_message('No {0} lint errors.'.format('next' if forward else 'previous'))

        return regionToSelect


class FindNextLintErrorCommand(FindLintErrorCommand):
    def run(self, edit):
        '''
        Move the cursor to the next lint error in the current view.
        The search will wrap to the top unless the sublimelinter_wrap_find
        setting is set to false.
        '''
        self.find_lint_error(forward=True)


class FindPreviousLintErrorCommand(FindLintErrorCommand):
    def run(self, edit):
        '''
        Move the cursor to the previous lint error in the current view.
        The search will wrap to the bottom unless the sublimelinter_wrap_find
        setting is set to false.
        '''
        self.find_lint_error(forward=False)


class SublimelinterWindowCommand(sublime_plugin.WindowCommand):
    def is_enabled(self):
        view = self.window.active_view()

        if view:
            if view.is_scratch():
                return False
            else:
                return True
        else:
            return False

    def run_(self, args):
        pass


class SublimelinterAnnotationsCommand(SublimelinterWindowCommand):
    '''Commands to extract annotations and display them in
       a file
    '''
    def run_(self, args):
        linter = LINTERS.get('annotations', None)

        if linter is None:
            return

        view = self.window.active_view()

        if not view:
            return

        text = view.substr(sublime.Region(0, view.size())).encode('utf-8')
        filename = (view.file_name() or '').encode('utf-8')
        notes = linter.extract_annotations(text, view, filename)
        _, filename = os.path.split(filename)
        annotations_view, _id = view_in_tab(view, 'Annotations from {0}'.format(filename), notes, '')


class SublimelinterCommand(SublimelinterWindowCommand):
    def is_enabled(self):
        enabled = super(SublimelinterCommand, self).is_enabled()

        if not enabled:
            return False

        linter = select_linter(self.window.active_view(), ignore_disabled=True)
        return linter is not None

    def run_(self, args={}):
        view = self.window.active_view()
        action = args.get('action', '')

        if view and action:
            if action == 'lint':
                self.lint_view(view, show_popup_list=args.get('show_popup', False))
            else:
                view.run_command('lint', action)

    def lint_view(self, view, show_popup_list):
        linter = select_linter(view, ignore_disabled=True)

        if linter:
            view.run_command('lint', linter.language)
            regions = get_lint_regions(view, coalesce=True)

            if regions:
                if show_popup_list:
                    popup_error_list(view)
                else:
                    sublime.error_message('{0} lint error{1}.'.format(len(regions), 's' if len(regions) != 1 else ''))
            else:
                sublime.error_message('No lint errors.')
        else:
            syntax = syntax_name(view)
            sublime.error_message('No linter for the syntax "{0}"'.format(syntax))


class SublimelinterLintCommand(SublimelinterCommand):
    def is_enabled(self):
        enabled = super(SublimelinterLintCommand, self).is_enabled()

        if enabled:
            view = self.window.active_view()

            if view and view.settings().get('sublimelinter') == True:
                return False

        return enabled


class SublimelinterShowErrorsCommand(SublimelinterCommand):
    def is_enabled(self):
        return super(SublimelinterShowErrorsCommand, self).is_enabled()


class SublimelinterEnableLoadSaveCommand(SublimelinterCommand):
    def is_enabled(self):
        enabled = super(SublimelinterEnableLoadSaveCommand, self).is_enabled()

        if enabled:
            view = self.window.active_view()

            if view and view.settings().get('sublimelinter') == 'load-save':
                return False

        return enabled


class SublimelinterEnableSaveOnlyCommand(SublimelinterCommand):
    def is_enabled(self):
        enabled = super(SublimelinterEnableSaveOnlyCommand, self).is_enabled()

        if enabled:
            view = self.window.active_view()

            if view and view.settings().get('sublimelinter') == 'save-only':
                return False

        return enabled


class SublimelinterDisableCommand(SublimelinterCommand):
    def is_enabled(self):
        enabled = super(SublimelinterDisableCommand, self).is_enabled()

        if enabled:
            view = self.window.active_view()

            if view and view.settings().get('sublimelinter') == False:
                return False

        return enabled


================================================
FILE: SublimeLinter.sublime-settings
================================================
/*
    SublimeLinter default settings
*/
{
    /*
        Sets the mode in which SublimeLinter runs:

        true - Linting occurs in the background as you type.
        false - Linting only occurs when you initiate it.
        "load-save" - Linting occurs only when a file is loaded and saved (the default).
        "save-only" - Linting occurs only when a file is saved.
    */
    "sublimelinter": "load-save",

    /*
        Maps language names **as listed at the beginning of the README** (but all lowercase)
        to executables for non-built in linters. If the executable is not in the default system path,
        or on posix systems is not in /usr/local/bin or ~/bin, then you must specify
        the full path to the executable. Note that paths in Windows must use double
        backslashes, for example "C:\\Program Files (x86)\\nodejs\\node.exe".

        Please note that the map _keys_ do not always match the name of the
        executable, but rather the language syntax for the executable to lint.

        This is the effective default map; your mappings may override these.

        "sublimelinter_executable_map":
        {
            "perl": "perl",
            "php": "php",
            "ruby": "ruby"
        },
    */
    "sublimelinter_executable_map":
    {
    },

    /*
        Maps syntax names to linters. This allows variations on a syntax
        (for example "Python (Django)") to be linted. The key is
        the name of the syntax **as it appears in the syntax list
        at the bottom right of the window**, and the value
        is the linter name **as listed in the README** (all lowercase)
        that the syntax maps to.
    */
    "sublimelinter_syntax_map":
    {
        "Python Django": "python",
        "Ruby on Rails": "ruby",
        "C++": "c"
    },

    // An array of linter names to disable. Names should be lowercase.
    "sublimelinter_disable":
    [
    ],

    /*
        The minimum delay in seconds (fractional seconds are okay) before
        a linter is run when the "sublimelinter" setting is true. This allows
        you to have background linting active, but defer the actual linting
        until you are idle. When this value is greater than the built in linting delay,
        errors are erased when the file is modified, since the assumption is
        you don't want to see errors while you type.
    */
    "sublimelinter_delay": 2,

    /*
        Selects the way the lines with errors or warnings are marked; "outline" draws
        outline boxes around the lines, "fill" fills the lines with the outline color,
        and "none" (default) disables all outline styles.
    */
    "sublimelinter_mark_style": "none",

    /*
        If true, lines with errors or warnings will be filled in with the
        outline color.

        This setting is DEPRECATED and will be ignored in future
        versions. Use "sublimelinter_mark_style" instead. For backwards
        compatibility reasons, this setting overrides "sublimelinter_mark_style"
        if that one is set to "outline", but has no effect if it's set to "none".
    */
    "sublimelinter_fill_outlines": false,

    // If true, lines with errors or warnings will have a gutter mark.
    "sublimelinter_gutter_marks": true,

    /*
        Choose the theme for gutter marks; available built-in options are:
        "alpha", "bright", "dark", "hard" and "simple"

        Anything else will be treated as a path to a set of images.
        For instance, setting the value to "../User/my-awesome-theme"
        would cause SublimeLinter to look for the images:
        "../User/my-awesome-theme-illegal.png",
        "../User/my-awesome-theme-violation.png",
        "../User/my-awesome-theme-warning.png"
        These images should all be approximately 32x32px.
    */
    "sublimelinter_gutter_marks_theme": "simple",

    // If true, the find next/previous error commands will wrap.
    "sublimelinter_wrap_find": true,

    // If true, when the file is saved any errors will appear in a popup list
    "sublimelinter_popup_errors_on_save": false,

    // JavaScript linter: "gjslint" to use the closure javascript linter (if available),
    // or either "jshint" or "jslint" to use a built in linter.
    "javascript_linter": "jshint",

    // jshint: options for linting JavaScript. See http://www.jshint.com/docs/#options for more info.
    // By deault, eval is allowed.
    "jshint_options":
    {
        // To fix column positions for JSHint errors you may want to add `"indent": 1` to your
        // **User** "jshint_options". This issue affects users with tabs for indentation.
        // This fix was reverted due to a conflict with using the `"white": true` option.
        // "indent": 1,
        "evil": true,
        "regexdash": true,
        "browser": true,
        "wsh": true,
        "trailing": true,
        "sub": true,
        "devel": true
    },

    // A list of command line options to send to gjslint. --nobeep is always sent.
    "gjslint_options":
    [
    ],

    // A list of gjslint error numbers to ignore. The list of error codes is here:
    // http://closure-linter.googlecode.com/svn/trunk/closure_linter/errors.py
    "gjslint_ignore":
    [
        110  // line too long
    ],

    // CSSLint options:
    // Each rule can have three values: error|warning|true|false
    // false => rule is disabled.
    // true => alias to 'error'
    // All rules are enabled by default.
    // Currently the only difference between warnings and errors is in the prefix of the message in the Sublime status bar.
    "csslint_options":
    {
        "adjoining-classes": "warning",
        "box-model": true,
        "box-sizing": "warning",
        "compatible-vendor-prefixes": "warning",
        "display-property-grouping": true,
        "duplicate-background-images": "warning",
        "duplicate-properties": true,
        "empty-rules": true,
        "errors": true,
        "fallback-colors": "warning",
        "floats": "warning",
        "font-faces": "warning",
        "font-sizes": "warning",
        "gradients": "warning",
        "ids": "warning",
        "import": "warning",
        "important": "warning",
        "known-properties": true,
        "outline-none": "warning",
        "overqualified-elements": "warning",
        "qualified-headings": "warning",
        "regex-selectors": "warning",
        "rules-count": "warning",
        "shorthand": "warning",
        "star-property-hack": "warning",
        "text-indent": "warning",
        "underscore-property-hack": "warning",
        "unique-headings": "warning",
        "universal-selector": "warning",
        "vendor-prefix": true,
        "zero-units": "warning"
    },

    // Set this to false to turn pep8 checking off completely
    "pep8": true,

    /*
        A list of pep8 error numbers to ignore. By default "line too long" errors are ignored.
        The list of error codes is in this file: https://github.com/jcrocholl/pep8/blob/master/pep8.py.
        Search for "Ennn:", where nnn is a 3-digit number.
    */
    "pep8_ignore":
    [
        "E501"
    ],

    /*
        If you use SublimeLinter for pyflakes checks, you can ignore some of the "undefined name xxx"
        errors (comes in handy if you work with post-processors, globals/builtins available only at runtime, etc.).
        You can control what names will be ignored with the user setting "pyflakes_ignore".

        Example:

        "pyflakes_ignore":
            [
                "some_custom_builtin_o_mine",
                "A_GLOBAL_CONSTANT"
            ],
    */
    "pyflakes_ignore":
    [
    ],

    /*
        Ordinarily pyflakes will issue a warning when 'from foo import *' is used,
        but it is ignored since the warning is not that helpful. If you want to see this warning,
        set this option to false.
    */
    "pyflakes_ignore_import_*": true,

    /*
        Perl linter: "perl" to use the Perl language syntax check, or "perlcritic" to use Perl::Critic linting.
        Perl is now set to use "perlcritic" by default due to a vulnerability with blindly running `perl -c`
        on files with `BEGIN` or `CHECK` blocks.
    */
    "perl_linter": "perlcritic",

    // Objective-J: if true, non-ascii characters are flagged as an error.
    "sublimelinter_objj_check_ascii": false,

    // Set to true to highlight annotations
    "sublimelinter_notes": false,

    // The set of annotation phrases to highlight
    "annotations": ["TODO", "README", "FIXME"]
}


================================================
FILE: changelog.txt
================================================
SublimeLinter 1.5.1 changelog
=============================

NEW FEATURES
------------
- SublimeLinter keeps its settings in its own settings file now:
  SublimeLinter.sublime-settings. You will need to copy your
  user settings to this file. To do so, follow these steps:

  1. Select "Preferences->Settings - User" in one tab/window.
     The title of this tab should be "Preferences.sublime-settings".
  2. Open another tab/window and select
     `Preferences->Package Settings->SublimeLinter->Settings - User`.
     The title of this window should be
     `SublimeLinter.sublime-settings`.
  3. Copy/cut any of the following settings from
     Preferences.sublime-settings to SublimeLinter.sublime-settings:

        sublimelinter
        sublimelinter_executable_map
        sublimelinter_syntax_map
        sublimelinter_disable
        sublimelinter_delay
        sublimelinter_fill_outlines
        sublimelinter_gutter_marks
        sublimelinter_wrap_find
        sublimelinter_popup_errors_on_save
        javascript_linter
        jshint_options
        pep8_ignore
        pyflakes_ignore
        pyflakes_ignore_import_*
        sublimelinter_objj_check_ascii

  4. Save SublimeLinter.sublime-settings. The changes may not take
     effect until you restart Sublime Text.

  When changes are made to the user SublimeLinter settings, they
  are immediately reloaded into every open view. Note that this will
  override any temporary changes you may have made to the settings in
  a given view.
- The google closure JavaScript linter (gjslint) is now supported.
  (https://developers.google.com/closure/utilities/docs/linter_howto)
  There is a new setting, `javascript_linter`, which determines which
  linter to use, jshint or gjslinter. You may also customize gjslint
  behavior with the `gjslint_options` and `gjslint_ignore` settings.
  Please select
  `Preferences->Package Settings->SublimeLinter->Settings - Default`
  for more information on these settings.
- The color theme names have been changed to avoid clashes with
  built in names.

    Old                     New
    ---------------------   -----------------------------
    sublimelinter.<type>    sublimelinter.outline.<type>
    invalid.<type>          sublimelinter.underline.<type>

  You will have to update your color themes accordingly. Please
  select `Preferences->Package Settings->SublimeLinter->README` and
  search for "Customizing colors" for more information.
- When selecting an error from the popup error list, the view is
  centered on the error line.

CHANGES/FIXES
-------------
- The PHP error regex has been updated to work with PHP 5.3.8 on Mac
  OS X.
- The popup error list will no longer choke on non-ASCII text.
- Selecting an error from the popup error list no longer attempts to
  go directly to the point of an error as this could not be done
  reliably. It will jump to the first non-whitespace character of
  the error's line.
- Go to next/previous error works correctly when an error line has
  no underlines.
- If an exception is thrown by jshint (e.g. too many errors), the
  errors captured up to that point are displayed.
- The built in jshint has been updated from the master jshint.
- Fixed errors that would occur with the popup error list when there
  was more than error on a line.


SublimeLinter 1.5.2 changelog
=============================

CHANGES/FIXES
-------------
- Fixed a problem with messages.json that prevented correct
  upgrading.

IMPORTANT
---------
Please check to see if you have multiple listings for `SublimeLinter`
in `Preferences -> Package Settings`. If you do see 2 listings,
please run `Package Control: Upgrade/Overwrite All Packages` from
the Command Palette (`Tools -> Command Palette`).


SublimeLinter 1.5.3 changelog
=============================

CHANGES/FIXES
-------------
- Annotations have been fixed.
- Entries in "sublimelinter_syntax_map" take precedence over built
  in mappings.
- Lint errors in PHP files will hopefully not be logged to the PHP
  log file.


SublimeLinter 1.5.4 changelog
=============================

CHANGES/FIXES
-------------
- jshint.js has been updated to the latest master version.
- [issue #128] An "unsafe" option has been added to jshint. If set
  true, any UTF-8 characters are allowed in the source.


SublimeLinter 1.5.5 changelog
=============================

CHANGES/FIXES
-------------
- This change log is available from the SublimeLinter preferences
  menu.


SublimeLinter 1.5.6 changelog
=============================

CHANGES/FIXES
-------------
- Fixed a problem with messages.json that prevented correct
  upgrading.

IMPORTANT
---------
Please check to see if you have multiple listings for `SublimeLinter`
in `Preferences -> Package Settings`. If you do see 2 listings,
please run `Package Control: Upgrade/Overwrite All Packages` from
the Command Palette (`Tools -> Command Palette`).


SublimeLinter 1.5.7 changelog
=============================

CHANGES/FIXES
-------------
- node.js is the preferred JavaScript engine on Mac OS X and will be
  used if it is installed. JavaScriptCore does not handle non-ASCII
  text correctly and you should install node.js if possible.
- If you imported `BaseLinter.JSC_PATH`, please change your linter to
  use the `self.jsc_path()` method instead. JSC_PATH should no
  longer be considered public.


SublimeLinter 1.6.0 changelog
=============================

NEW FEATURES
------------
- Simpler abstraction of JavaScript engines for JS powered linters.

  To leverage a JS linter, include a "linter.js" file; this file
  should `require` the actual linter library file and export a `lint`
  function. The `lint` function should return a list of errors back
  to the python language handler file (via the `errors` parameter to
  the `parse_errors` method).

  Although "linter.js" should follow the Node.js api, the linter may
  also be run via JavaScriptCore on OS X if Node.js is not installed.
  In the case where JavaScriptCore is used, require + export are
  shimmed to keep things consistent. However, it is important not to
  assume that a full Node.js api is available. If you must know what
  JS engine you are using, you may check for `USING_JSC` to be set
  as `true` when JavaScriptCore is used.

  For examples of using the JS engines, see "csslint", "jslint", and
  "jshint" in "SublimeLinter/sublimelinter/modules/libs" and the
  respective python code of "css.py" and "javascript.py" in
  "SublimeLinter/sublimelinter/modules".
- Douglas Crockford's [JSLint](http://jslint.com) JavaScript linter
  is now supported. To use JSLint set the "javascript_linter" setting
  to "jslint". You may also customize jslint behavior with the
  "jslint_options" setting. For more information about options
  available to JSLint, see http://jslint.com/lint.html.
- The [CSSLint](http://csslint.net) CSS linter is now supported.
  By default all CSSLint settings are turned on. You may customize
  csslint behavior with the "csslint_options" setting. Please select
  "Preferences->Package Settings->SublimeLinter->Settings - Default"
  for more information on turning off or adjusting severity of tests.
  For more information about options available to CSSLint, see
  https://github.com/stubbornella/csslint/wiki/Rules.


SublimeLinter 1.6.1 changelog
=============================

CHANGES/FIXES
-------------
- Fixed an issue (#141) with JSLint running in Node.js
- Updated CSSLint, JSLint, JSHint to latest stable releases.
- Added additional debugging output (in Sublime console) when
  errors occur running linters written in JavaScript.


SublimeLinter 1.6.2 changelog
=============================

CHANGES/FIXES
-------------
- Replaced the default perl linter with Perl::Critic. The standard
  Perl syntax checker can still be invoked by switching the
  "perl_linter" setting to "perl".
- Added a LICENSE file to define appropriate usage of SublimeLinter
  and its source.
- Converted README back to markdown.

IMPORTANT
---------
Due to a vulnerability (issue #77) with the Perl linter, Perl syntax
checking is no longer enabled by default. The default linter for
Perl has been replaced by Perl::Critic.


SublimeLinter 1.6.3 changelog
=============================

NEW FEATURES
------------
- Support for `.jshintrc` files. If using JSHint, SublimeLinter
  will recursively search the directory tree (from the file location
  to the file-system root directory). This functionality is
  specified in the JSHint README.
  https://github.com/jshint/node-jshint/#within-your-projects-directory-tree

CHANGES/FIXES
-------------
- Fixed README reference in the menu.
- Updated CoffeeScript module to be compatible with the updated
  coffee command in version 1.3.

IMPORTANT
---------
If you are using the CoffeeScript linting, please upgrade
the installed coffee-script NPM module to 1.3 or greater.

    npm update -g coffee-script


SublimeLinter 1.6.4 changelog
=============================

IMPORTANT!!
-----------
Please note that the SublimeLinter repository has moved to:

    https://github.com/SublimeLinter/SublimeLinter

Issues and pull requests should be made there.

NEW FEATURES
------------
- The Objective-J linter now catches spaces inside parentheses
  and dependent clauses on the same line as a control structure.

CHANGES/FIXES
-------------
- The README has been reorganized to hopefully be clearer.
- More explicit Node.js installation instructions have been provided.
- The "pep8" setting is now recognized in SublimeLinter's settings.
- When a minimum delay is specified with the "sublimelinter_delay"
  setting, SublimeLinter will only lint the currently displayed file
  when the queued linters run. This allows you to avoid linting of
  files as they are selected in the choose file palette.


SublimeLinter 1.6.5 changelog
=============================

NEW FEATURES
------------
- Added a (Ruby) Haml linter based on `haml -c`. For more
  information about Haml, please see http://haml.info.
- Added a simple Git commit message linter. This linter follows the
  rules as defined by
  http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

CHANGES/FIXES
-------------
- Updated several links to point to the SublimeLinter's new Github
  location.
- "Ruby on Rails" syntax maps to "ruby" as part of the default
  settings.
- Linter arguments are now consistently defined as arrays (instead
  of tuples).
- Syntax map settings are no longer (sometimes) case-sensitive.


SublimeLinter 1.6.6 changelog
=============================

CHANGES/FIXES
-------------
- JSHint now shows underlines at the appropriate character positions
  when using tabs for indentation.
- Upgrading CSSLint to the latest version (v0.9.8). This adds
  support for the latest "Compatibility" options: "Disallow star
  hack" and "Disallow underscore hack".
- Annotation highlighting is working again.
- Git Commit Message linting now ignores `git --diff` output in
  messages. These lines are automatically generated and inserted
  when running `git commit -v`.


SublimeLinter 1.6.7 changelog
=============================

NEW FEATURES
------------
- Puppet linting is now supported via `puppet parser validate`.
- Added an option for more granular control of outline decorations.
  Set the value of "sublimelinter_mark_style" to "outline", "fill",
  or "none" in the user settings.

CHANGES/FIXES
-------------
- Repaired the built-in CSS linter (CSSLint). This was broken with
  with the last update.
- Added missing documentation for "save-only" linting in the
  settings file.
- Adjusted ambiguous/misleading documentation for the
  "sublimelinter_executable_map" setting.


SublimeLinter 1.6.8 changelog
=============================

NEW FEATURES
------------
- HTML5 linting support via `tidy`. This linter will not run unless
  you have a version of tidy with HTML5 support. To use this linter,
  please see: https://github.com/w3c/tidy-html5
- XML linting via `xmllint`.

CHANGES/FIXES
-------------
- Made significant progress on issue (#181). However, SublimeLinter
  still throws with some linter types on Windows 7 when a user has
  non-ascii characters in the path (to the SL plugin).
- Updated PEP8 to v1.1
- Updated Pyflakes to v0.5.0
- Updated JSHint to latest stable (r11).
- Reverted a fix for accurate (JSHint) error column positions (when
  using tab indentation) due to a regression with the `"white": true`
  option. You may still manually fix error positions by setting
  `"indent": 1`.
- Changed (the default) background linting delay to a more sane 2
  seconds. This reduces memory usage, cpu processing, and visual
  noise while you are actively writing code.


SublimeLinter 1.6.9 changelog
=============================

NEW FEATURES
------------
- C/C++ lint via `cppcheck`. Also added alternative (hidden) support
  for `cpplint.py`. Please see README for more info.
- Lua syntax check via `luac`.

CHANGES/FIXES
-------------
- Adding a 'beta' channel for SublimeLinter into Package Control.
  This branch will act as a more formal method for testing new
  features and fixes before a release.


SublimeLinter 1.6.10 changelog
==============================

CHANGES/FIXES
-------------
- Puppet validation supports error output for Puppet v3.0+.
- JSHint options now support the (proper) "globals" definition.
- Lua syntax check no longer creates luac.out file clutter.
- Clarified documentation for styling sublimelinter.annotations.


SublimeLinter 1.6.11 changelog
==============================

CHANGES/FIXES
-------------
- Github (nodeload) zip url scheme changed.


SublimeLinter 1.6.12 changelog
==============================

CHANGES/FIXES
-------------
- Cpplint no longer uses a temporary file - it leverages stdin
  instead.
- Fixed issue #298: C linter throws "KeyError"


SublimeLinter 1.6.13 changelog
==============================

CHANGES/FIXES
-------------
- Updated PEP8 to version v1.4
- Updated PyFlakes to v0.6.1
- Updated CSSLint to v0.9.10
- Update JSHint to v1.0.0
- Update JSLint to latest
- Fixed issues with attempting to encode file name when using
  scratch space.


SublimeLinter 1.7.0 changelog
=============================

NEW FEATURES
------------
- Add "Quick Start" section to the README
- New commands for the (Sublime Text) command palette to open the
  SublimeLinter preferences files (Default & User)
- Ruby linting support via [ruby-lint](https://github.com/YorickPeterse/ruby-lint)
- Haskell linting support via [hlint](http://community.haskell.org/~ndm/hlint/)
- Add gutter mark themes. Add 5 new built-in themes:
  "alpha", "bright", "dark", "hard", and "simple".
  Custom themes can also be used.

CHANGES/FIXES
-------------
- Update JSHint to v1.1.0
- Update CoffeeScript linter to support latest error message format
- Settings are refreshed when a new file is saved; this should fix
  several bugs: #233, #359, #149, #367
- The `lint_args` setting now matches the documentation; it will
  override the default arguments rather than extend them. This will
  give users full control over linter/checker arguments.
- Clean up markdown formatting in changelog

IMPORTANT
---------
Due to the fix for `lint_args`, if you are currently customizing this
setting, you **must** adjust your settings.


SublimeLinter 1.7.1 changelog
=============================

CHANGES/FIXES
-------------
- Update JSLint to latest
- Update JSHint to v2.1.5
- Updated link to JSHint documentation
- Updated PEP8 to v1.4.6
- Updated Pyflakes to v0.7.3
- Gutter mark icons are now retina quality
- Included Gutter mark icon PSD (for tinkering :)


SublimeLinter 1.7.2 changelog
=============================

CHANGES/FIXES
-------------
- Update JSHint to v2.1.8
- Fixing compatibility regression with latest JSHint
- Fixing compatibility regression with latest Pyflakes
- Apologizing for hasty v1.7.1 release ;)


================================================
FILE: messages/1.5.1.txt
================================================
SublimeLinter 1.5.1 changelog
=============================

NEW FEATURES
------------
- SublimeLinter keeps its settings in its own settings file now:
  SublimeLinter.sublime-settings. You will need to copy your
  user settings to this file. To do so, follow these steps:
  
  1. Select "Preferences->Settings - User" in one tab/window.
     The title of this tab should be "Preferences.sublime-settings".
  2. Open another tab/window and select
     `Preferences->Package Settings->SublimeLinter->Settings - User`.
     The title of this window should be
     `SublimeLinter.sublime-settings`.
  3. Copy/cut any of the following settings from
     Preferences.sublime-settings to SublimeLinter.sublime-settings:

        sublimelinter
        sublimelinter_executable_map
        sublimelinter_syntax_map
        sublimelinter_disable
        sublimelinter_delay
        sublimelinter_fill_outlines
        sublimelinter_gutter_marks
        sublimelinter_wrap_find
        sublimelinter_popup_errors_on_save
        javascript_linter
        jshint_options
        pep8_ignore
        pyflakes_ignore
        pyflakes_ignore_import_*
        sublimelinter_objj_check_ascii

  4. Save SublimeLinter.sublime-settings. The changes may not take
     effect until you restart Sublime Text.

  When changes are made to the user SublimeLinter settings, they
  are immediately reloaded into every open view. Note that this will
  override any temporary changes you may have made to the settings in
  a given view.
- The google closure JavaScript linter (gjslint) is now supported.
  (https://developers.google.com/closure/utilities/docs/linter_howto)
  There is a new setting, `javascript_linter`, which determines which
  linter to use, jshint or gjslinter. You may also customize gjslint
  behavior with the `gjslint_options` and `gjslint_ignore` settings.
  Please select
  `Preferences->Package Settings->SublimeLinter->Settings - Default`
  for more information on these settings.
- The color theme names have been changed to avoid clashes with
  built in names.

    Old                     New
    ---------------------   -----------------------------
    sublimelinter.<type>    sublimelinter.outline.<type>
    invalid.<type>          sublimelinter.underline.<type>

  You will have to update your color themes accordingly. Please 
  select `Preferences->Package Settings->SublimeLinter->README` and 
  search for "Customizing colors" for more information.
- When selecting an error from the popup error list, the view is 
  centered on the error line.

CHANGES/FIXES
-------------
- The PHP error regex has been updated to work with PHP 5.3.8 on Mac
  OS X.
- The popup error list will no longer choke on non-ASCII text.
- Selecting an error from the popup error list no longer attempts to
  go directly to the point of an error as this could not be done 
  reliably. It will jump to the first non-whitespace character of 
  the error's line.
- Go to next/previous error works correctly when an error line has
  no underlines.
- If an exception is thrown by jshint (e.g. too many errors), the 
  errors captured up to that point are displayed.
- The built in jshint has been updated from the master jshint.
- Fixed errors that would occur with the popup error list when there 
  was more than error on a line.



================================================
FILE: messages/1.5.2.txt
================================================
SublimeLinter 1.5.2 changelog
=============================

CHANGES/FIXES
-------------
- Fixed a problem with messages.json that prevented correct
  upgrading.

IMPORTANT
---------
Please check to see if you have multiple listings for `SublimeLinter`
in `Preferences -> Package Settings`. If you do see 2 listings, 
please run `Package Control: Upgrade/Overwrite All Packages` from 
the Command Palette (`Tools -> Command Palette`).


================================================
FILE: messages/1.5.3.txt
================================================
SublimeLinter 1.5.3 changelog
=============================

CHANGES/FIXES
-------------
- Annotations have been fixed.
- Entries in "sublimelinter_syntax_map" take precedence over built
  in mappings.
- Lint errors in PHP files will hopefully not be logged to the PHP 
  log file.


================================================
FILE: messages/1.5.4.txt
================================================
SublimeLinter 1.5.4 changelog
=============================

CHANGES/FIXES
-------------
- jshint.js has been updated to the latest master version.
- [issue #128] An "unsafe" option has been added to jshint. If set 
  true, any UTF-8 characters are allowed in the source.


================================================
FILE: messages/1.5.5.txt
================================================
SublimeLinter 1.5.5 changelog
=============================

CHANGES/FIXES
-------------
- This change log is available from the SublimeLinter preferences 
  menu.


================================================
FILE: messages/1.5.6.txt
================================================
SublimeLinter 1.5.6 changelog
=============================

CHANGES/FIXES
-------------
- Fixed a problem with messages.json that prevented correct
  upgrading.

IMPORTANT
---------
Please check to see if you have multiple listings for `SublimeLinter`
in `Preferences -> Package Settings`. If you do see 2 listings, 
please run `Package Control: Upgrade/Overwrite All Packages` from 
the Command Palette (`Tools -> Command Palette`).


================================================
FILE: messages/1.5.7.txt
================================================
SublimeLinter 1.5.7 changelog
=============================

CHANGES/FIXES
-------------
- node.js is the preferred JavaScript engine on Mac OS X and will be 
  used if it is installed. JavaScriptCore does not handle non-ASCII 
  text correctly and you should install node.js if possible.
- If you imported `BaseLinter.JSC_PATH`, please change your linter to
  use the `self.jsc_path()` method instead. JSC_PATH should no 
  longer be considered public.


================================================
FILE: messages/1.6.0.txt
================================================
SublimeLinter 1.6.0 changelog
=============================

NEW FEATURES
------------
- Simpler abstraction of JavaScript engines for JS powered linters.

  To leverage a JS linter, include a "linter.js" file; this file 
  should `require` the actual linter library file and export a `lint`
  function. The `lint` function should return a list of errors back 
  to the python language handler file (via the `errors` parameter to 
  the `parse_errors` method).

  Although "linter.js" should follow the Node.js api, the linter may 
  also be run via JavaScriptCore on OS X if Node.js is not installed.
  In the case where JavaScriptCore is used, require + export are 
  shimmed to keep things consistent. However, it is important not to 
  assume that a full Node.js api is available. If you must know what 
  JS engine you are using, you may check for `USING_JSC` to be set 
  as `true` when JavaScriptCore is used.
  
  For examples of using the JS engines, see "csslint", "jslint", and
  "jshint" in "SublimeLinter/sublimelinter/modules/libs" and the 
  respective python code of "css.py" and "javascript.py" in
  "SublimeLinter/sublimelinter/modules".
- Douglas Crockford's [JSLint](http://jslint.com) JavaScript linter
  is now supported. To use JSLint set the "javascript_linter" setting
  to "jslint". You may also customize jslint behavior with the
  "jslint_options" setting. For more information about options 
  available to JSLint, see http://jslint.com/lint.html.
- The [CSSLint](http://csslint.net) CSS linter is now supported.
  By default all CSSLint settings are turned on. You may customize
  csslint behavior with the "csslint_options" setting. Please select
  "Preferences->Package Settings->SublimeLinter->Settings - Default"
  for more information on turning off or adjusting severity of tests.
  For more information about options available to CSSLint, see
  https://github.com/stubbornella/csslint/wiki/Rules.


================================================
FILE: messages/1.6.1.txt
================================================
SublimeLinter 1.6.1 changelog
=============================

CHANGES/FIXES
-------------
- Fixed an issue (#141) with JSLint running in Node.js
- Updated CSSLint, JSLint, JSHint to latest stable releases.
- Added additional debugging output (in Sublime console) when
  errors occur running linters written in JavaScript.


================================================
FILE: messages/1.6.10.txt
================================================
SublimeLinter 1.6.10 changelog
==============================

CHANGES/FIXES
-------------
- Puppet validation supports error output for Puppet v3.0+.
- JSHint options now support the (proper) "globals" definition.
- Lua syntax check no longer creates luac.out file clutter.
- Clarified documentation for styling sublimelinter.annotations.


================================================
FILE: messages/1.6.11.txt
================================================
SublimeLinter 1.6.11 changelog
==============================

CHANGES/FIXES
-------------
- Github (nodeload) zip url scheme changed.


================================================
FILE: messages/1.6.12.txt
================================================
SublimeLinter 1.6.12 changelog
==============================

CHANGES/FIXES
-------------
- Cpplint no longer uses a temporary file - it leverages stdin
  instead.
- Fixed issue #298: C linter throws "KeyError"


================================================
FILE: messages/1.6.13.txt
================================================
SublimeLinter 1.6.13 changelog
==============================

CHANGES/FIXES
-------------
- Updated PEP8 to version v1.4
- Updated PyFlakes to v0.6.1
- Updated CSSLint to v0.9.10
- Update JSHint to v1.0.0
- Update JSLint to latest
- Fixed issues with attempting to encode file name when using 
  scratch space.


================================================
FILE: messages/1.6.2.txt
================================================
SublimeLinter 1.6.2 changelog
=============================

CHANGES/FIXES
-------------
- Replaced the default perl linter with Perl::Critic. The standard 
  Perl syntax checker can still be invoked by switching the 
  "perl_linter" setting to "perl".
- Added a LICENSE file to define appropriate usage of SublimeLinter 
  and its source.
- Converted README back to markdown.

IMPORTANT
---------
Due to a vulnerability (issue #77) with the Perl linter, Perl syntax
checking is no longer enabled by default. The default linter for
Perl has been replaced by Perl::Critic.


================================================
FILE: messages/1.6.3.txt
================================================
SublimeLinter 1.6.3 changelog
=============================

NEW FEATURES
------------
- Support for `.jshintrc` files. If using JSHint, SublimeLinter
  will recursively search the directory tree (from the file location
  to the file-system root directory). This functionality is
  specified in the JSHint README.
  https://github.com/jshint/node-jshint/#within-your-projects-directory-tree

CHANGES/FIXES
-------------
- Fixed README reference in the menu.
- Updated CoffeeScript module to be compatible with the updated
  coffee command in version 1.3.

IMPORTANT
---------
If you are using the CoffeeScript linting, please upgrade
the installed coffee-script NPM module to 1.3 or greater.

    npm update -g coffee-script


================================================
FILE: messages/1.6.4.txt
================================================
SublimeLinter 1.6.4 changelog
=============================

IMPORTANT!!
-----------
Please note that the SublimeLinter repository has moved to:

    https://github.com/SublimeLinter/SublimeLinter

Issues and pull requests should be made there.

NEW FEATURES
------------
- The Objective-J linter now catches spaces inside parentheses
  and dependent clauses on the same line as a control structure.

CHANGES/FIXES
-------------
- The README has been reorganized to hopefully be clearer.
- More explicit Node.js installation instructions have been provided.
- The "pep8" setting is now recognized in SublimeLinter's settings.
- When a minimum delay is specified with the "sublimelinter_delay" 
  setting, SublimeLinter will only lint the currently displayed file 
  when the queued linters run. This allows you to avoid linting of 
  files as they are selected in the choose file palette.


================================================
FILE: messages/1.6.5.txt
================================================
SublimeLinter 1.6.5 changelog
=============================

NEW FEATURES
------------
- Added a (Ruby) Haml linter based on `haml -c`. For more 
  information about Haml, please see http://haml.info.
- Added a simple Git commit message linter. This linter follows the
  rules as defined by
  http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

CHANGES/FIXES
-------------
- Updated several links to point to the SublimeLinter's new Github
  location.
- "Ruby on Rails" syntax maps to "ruby" as part of the default
  settings.
- Linter arguments are now consistently defined as arrays (instead
  of tuples).
- Syntax map settings are no longer (sometimes) case-sensitive.


================================================
FILE: messages/1.6.6.txt
================================================
SublimeLinter 1.6.6 changelog
=============================

CHANGES/FIXES
-------------
- JSHint now shows underlines at the appropriate character positions 
  when using tabs for indentation.
- Upgrading CSSLint to the latest version (v0.9.8). This adds 
  support for the latest "Compatibility" options: "Disallow star 
  hack" and "Disallow underscore hack".
- Annotation highlighting is working again.
- Git Commit Message linting now ignores `git --diff` output in
  messages. These lines are automatically generated and inserted
  when running `git commit -v`.


================================================
FILE: messages/1.6.7.txt
================================================
SublimeLinter 1.6.7 changelog
=============================

NEW FEATURES
------------
- Puppet linting is now supported via `puppet parser validate`.
- Added an option for more granular control of outline decorations. 
  Set the value of "sublimelinter_mark_style" to "outline", "fill", 
  or "none" in the user settings.

CHANGES/FIXES
-------------
- Repaired the built-in CSS linter (CSSLint). This was broken with
  with the last update.
- Added missing documentation for "save-only" linting in the
  settings file.
- Adjusted ambiguous/misleading documentation for the
  "sublimelinter_executable_map" setting.


================================================
FILE: messages/1.6.8.txt
================================================
SublimeLinter 1.6.8 changelog
=============================

NEW FEATURES
------------
- HTML5 linting support via `tidy`. This linter will not run unless
  you have a version of tidy with HTML5 support. To use this linter, 
  please see: https://github.com/w3c/tidy-html5
- XML linting via `xmllint`.

CHANGES/FIXES
-------------
- Made significant progress on issue (#181). However, SublimeLinter 
  still throws with some linter types on Windows 7 when a user has 
  non-ascii characters in the path (to the SL plugin).
- Updated PEP8 to v1.1
- Updated Pyflakes to v0.5.0
- Updated JSHint to latest stable (r11).
- Reverted a fix for accurate (JSHint) error column positions (when 
  using tab indentation) due to a regression with the `"white": true`
  option. You may still manually fix error positions by setting
  `"indent": 1`.
- Changed (the default) background linting delay to a more sane 2
  seconds. This reduces memory usage, cpu processing, and visual 
  noise while you are actively writing code.


================================================
FILE: messages/1.6.9.txt
================================================
SublimeLinter 1.6.9 changelog
=============================

NEW FEATURES
------------
- C/C++ lint via `cppcheck`. Also added alternative (hidden) support 
  for `cpplint.py`. Please see README for more info.
- Lua syntax check via `luac`.

CHANGES/FIXES
-------------
- Adding a 'beta' channel for SublimeLinter into Package Control.
  This branch will act as a more formal method for testing new
  features and fixes before a release.


================================================
FILE: messages/1.7.0.txt
================================================
SublimeLinter 1.7.0 changelog
=============================

NEW FEATURES
------------
- Add "Quick Start" section to the README
- New commands for the (Sublime Text) command palette to open the 
  SublimeLinter preferences files (Default & User)
- Ruby linting support via [ruby-lint](https://github.com/YorickPeterse/ruby-lint)
- Haskell linting support via [hlint](http://community.haskell.org/~ndm/hlint/)
- Add gutter mark themes. Add 5 new built-in themes:
  "alpha", "bright", "dark", "hard", and "simple".
  Custom themes can also be used.

CHANGES/FIXES
-------------
- Update JSHint to v1.1.0
- Update CoffeeScript linter to support latest error message format
- Settings are refreshed when a new file is saved; this should fix
  several bugs: #233, #359, #149, #367
- The `lint_args` setting now matches the documentation; it will
  override the default arguments rather than extend them. This will
  give users full control over linter/checker arguments.
- Clean up markdown formatting in changelog

IMPORTANT
---------
Due to the fix for `lint_args`, if you are currently customizing this
setting, you **must** adjust your settings.


================================================
FILE: messages/1.7.1.txt
================================================
SublimeLinter 1.7.1 changelog
=============================

CHANGES/FIXES
-------------
- Update JSLint to latest
- Update JSHint to v2.1.5
- Updated link to JSHint documentation
- Updated PEP8 to v1.4.6
- Updated Pyflakes to v0.7.3
- Gutter mark icons are now retina quality
- Included Gutter mark icon PSD (for tinkering :)


================================================
FILE: messages/1.7.2.txt
================================================
SublimeLinter 1.7.2 changelog
=============================

CHANGES/FIXES
-------------
- Update JSHint to v2.1.8
- Fixing compatibility regression with latest JSHint
- Fixing compatibility regression with latest Pyflakes
- Apologizing for hasty v1.7.1 release ;)


================================================
FILE: messages/SublimeLinter3-update1.txt
================================================
=====================
SublimeLinter for ST3
Fundraising update
=====================

As of October 28, we have raised $980 (after fees) from 90 donations.
This is enough to get started, but I was hoping for twice that amount.
If you are one of the thousands of happy SublimeLinter users who has not donated,
please consider making a small donation to keep this plugin alive.
Open source software is not free! It is we the developers who usually
end up paying for it.

To donate and find out more about what you can expect from SublimeLinter3,
go here:

https://github.com/SublimeLinter/SublimeLinter3#sublimelinter3

If you are interested in helping with development, please leave a message
here:

https://github.com/SublimeLinter/SublimeLinter3/issues/3

Thanks again for your support,

- Aparajita Fishman
  SublimeLinter maintainer


================================================
FILE: messages/SublimeLinter3-update2.txt
================================================
=====================
SublimeLinter for ST3
Fundraising update
October 31, 2013
=====================

Through October 30, we have raised $2885 (after fees) from 226 donations. Thank you for your support!
Development has started, and I’m very excited about this new version! But there is still a lot of work
to do, and I’m getting cool new ideas every day.

So if you are one of the thousands of happy SublimeLinter users who has not donated yet, it isn’t too late
to contribute. Please consider making a small donation to fund ongoing development and to compensate
Ryan Hileman for the great work on which SublimeLinter3 is based.

Open source software is not free! It is we the developers who usually end up paying for it.
To donate and find out more about what you can expect from SublimeLinter3, go here:

https://github.com/SublimeLinter/SublimeLinter3#sublimelinter3

If you are interested in helping with development, please leave a message here:

https://github.com/SublimeLinter/SublimeLinter3/issues/3

Thanks again for your support,

- Aparajita Fishman
  SublimeLinter maintainer


================================================
FILE: messages/SublimeLinter3-update3.txt
================================================

  ____        _     _ _                _     _       _              _____
 / ___| _   _| |__ | (_)_ __ ___   ___| |   (_)_ __ | |_ ___ _ __  |___ /
 \___ \| | | | '_ \| | | '_ ` _ \ / _ \ |   | | '_ \| __/ _ \ '__|   |_ \
  ___) | |_| | |_) | | | | | | | |  __/ |___| | | | | ||  __/ |     ___) |
 |____/ \__,_|_.__/|_|_|_| |_| |_|\___|_____|_|_| |_|\__\___|_|    |____/

                           _
                          | |__   __ _ ___
                          | '_ \ / _` / __|
                          | | | | (_| \__ \
                          |_| |_|\__,_|___/


                   _                 _          _ _
                  | | __ _ _ __   __| | ___  __| | |
                  | |/ _` | '_ \ / _` |/ _ \/ _` | |
                  | | (_| | | | | (_| |  __/ (_| |_|
                  |_|\__,_|_| |_|\__,_|\___|\__,_(_)


SublimeLinter for Sublime Text 3 is here, and it’s soooooo much better than before!
Install it from Package Control and enjoy!

Unless someone else comes forward, SublimeLinter for Sublime Text 2 will no longer
be supported. I strongly encourage everyone to upgrade to Sublime Text 3 and
SublimeLinter 3 — you’ll be glad you did! Take a look at the extensive documentation
to see the great new features in SublimeLinter 3.

https://github.com/SublimeLinter/SublimeLinter3


*****************
 Share the love!
*****************

I spent hundreds of hours writing and documenting SublimeLinter 3 to make it the best
it can be — easy to use, easy to configure, easy to update, easy to extend. If you use
SublimeLinter and feel it is making your coding life better and easier, please consider
making a donation to help fund development and support. Thank you!

To donate: https://github.com/SublimeLinter/SublimeLinter3#share-the-love

Thank you for your support!

- Aparajita Fishman
  SublimeLinter maintainer


================================================
FILE: messages/SublimeLinter3.txt
================================================
=====================
SublimeLinter for ST3
We need your help!
=====================

According to the Package Control site, SublimeLinter is the 4th most popular
plugin for Sublime Text, with 224K installs. That's an incredible number.
Thank you to everyone!

We all want SublimeLinter to become fully compatible with Sublime Text 3,
and we want it to be better supported. In moving to ST3 (and python 3),
we decided to start from scratch with a new architecture (based on sublimelint)
that allows us to get the most out of both ST3 and python 3. And we plan
to join forces with Ryan Hileman and merge sublimelint and SublimeLinter,
so we can pool our resources.

To find out more about what you can expect from SublimeLinter3,
read about it here:

https://github.com/SublimeLinter/SublimeLinter3#sublimelinter3

To make this happen any time soon, we need your help. I have lots of other
projects that currently have a higher priority than SublimeLinter3. If you
value SublimeLinter and want to see it running on ST3 sooner rather than later,
please consider donating. If we can raise $3-5K, I'll move SublimeLinter3
to top of my priority list and finish it off within a week or two.

See the link above for info on donating.

Thanks again,

- Aparajita Fishman
  SublimeLinter maintainer


================================================
FILE: messages/install.txt
================================================
SublimeLinter
=============

SublimeLinter is a plugin that supports "lint" programs (known as "linters"). SublimeLinter highlights
lines of code the linter deems to contain (potential) errors. It also
supports highlighting special annotations (for example: TODO) so that they
can be quickly located.

SublimeLinter has built in linters for the following languages:

* C/C++ - lint via `cppcheck`
* CoffeeScript - lint via `coffee -s -l`
* CSS - lint via built-in [csslint](http://csslint.net)
* Git Commit Messages - lint via built-in module based on [A Note About Git Commit Messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
* Haml - syntax check via `haml -c`
* HTML - lint via `tidy` (actually [tidy for HTML5](http://w3c.github.com/tidy-html5/))
* Java - lint via `javac -Xlint`
* JavaScript - lint via built in [jshint](http://jshint.org), [jslint](http://jslint.com), or the [closure linter (gjslint)](https://developers.google.com/closure/utilities/docs/linter_howto) (if installed)
* Lua - syntax check via `luac`
* Objective-J - lint via built-in [capp_lint](https://github.com/aparajita/capp_lint)
* Perl - lint via [Perl:Critic](http://perlcritic.com/) or syntax+deprecation check via `perl -c`
* PHP - syntax check via `php -l`
* Puppet - syntax check via `puppet parser validate`
* Python - native, moderately-complete lint
* Ruby - syntax check via `ruby -wc`
* XML - lint via `xmllint`


For more information:
---------------------
Please take the time to read the documentation:

* Online - https://github.com/SublimeLinter/SublimeLinter
* Sublime Text - Select Preferences->Package Settings->SublimeLinter->README


IMPORTANT

Do NOT edit the default SublimeLinter settings. Your changes will be lost
when SublimeLinter is updated. ALWAYS edit the user SublimeLinter settings
by selecting "Preferences->Package Settings->SublimeLinter->Settings - User".
Note that individual settings you include in your user settings will **completely**
replace the corresponding default setting, so you must provide that setting in its entirety.


================================================
FILE: messages.json
================================================
{
    "install": "messages/install.txt",
    "1.5.1": "messages/1.5.1.txt",
    "1.5.2": "messages/1.5.2.txt",
    "1.5.3": "messages/1.5.3.txt",
    "1.5.4": "messages/1.5.4.txt",
    "1.5.5": "messages/1.5.5.txt",
    "1.5.6": "messages/1.5.6.txt",
    "1.5.7": "messages/1.5.7.txt",
    "1.6.0": "messages/1.6.0.txt",
    "1.6.1": "messages/1.6.1.txt",
    "1.6.2": "messages/1.6.2.txt",
    "1.6.3": "messages/1.6.3.txt",
    "1.6.4": "messages/1.6.4.txt",
    "1.6.5": "messages/1.6.5.txt",
    "1.6.6": "messages/1.6.6.txt",
    "1.6.7": "messages/1.6.7.txt",
    "1.6.8": "messages/1.6.8.txt",
    "1.6.9": "messages/1.6.9.txt",
    "1.6.10": "messages/1.6.10.txt",
    "1.6.11": "messages/1.6.11.txt",
    "1.6.12": "messages/1.6.12.txt",
    "1.6.13": "messages/1.6.13.txt",
    "1.7.0": "messages/1.7.0.txt",
    "1.7.1": "messages/1.7.1.txt",
    "1.7.2": "messages/1.7.2.txt",
    "1.7.2+1": "messages/SublimeLinter3.txt",
    "1.7.2+3": "messages/SublimeLinter3-update1.txt",
    "1.7.2+4": "messages/SublimeLinter3-update2.txt",
    "1.7.2+5": "messages/SublimeLinter3-update3.txt"
}


================================================
FILE: package_control.json
================================================
{
	"schema_version": "1.1",
	"packages": [
		{
			"name": "SublimeLinter",
			"description": "Inline lint highlighting for the Sublime Text 2 editor",
			"author": "Kronuz, Aparajita Fishman, Jake Swartwood",
			"homepage": "http://github.com/SublimeLinter/SublimeLinter",
			"platforms": {
				"*": [
					{
						"version": "1.7.2+4",
						"url": "https://nodeload.github.com/SublimeLinter/SublimeLinter/zip/v1.7.2+4"
					}
				]
			}
		},
		{
			"name": "SublimeLinter Beta",
			"description": "This version is considered unstable; only install this package if you plan on testing",
			"author": "Kronuz, Aparajita Fishman, Jake Swartwood",
			"homepage": "https://github.com/SublimeLinter/SublimeLinter/tree/beta",
			"platforms": {
				"*": [
					{
						"version": "1.7.3-beta.0",
						"url": "https://nodeload.github.com/SublimeLinter/SublimeLinter/zip/beta"
					}
				]
			}
		}
	]
}


================================================
FILE: sublimelinter/__init__.py
================================================


================================================
FILE: sublimelinter/loader.py
================================================
# Note: Unlike linter modules, changes made to this module will NOT take effect until
# Sublime Text is restarted.

import glob
import os
import os.path
import sys

import modules.base_linter as base_linter

# sys.path appears to ignore individual paths with unicode characters.
# This means that this lib_path will be ignored for Windows 7 users with
# non-ascii characters in their username (thus as their home directory).
#
# libs_path = os.path.abspath(os.path.join(os.path.dirname(__file__.encode('utf-8')), u'modules', u'libs'))
#
# if libs_path not in sys.path:
#     sys.path.insert(0, libs_path)

# As a fix for the Windows 7 lib path issue (#181), the individual modules in
# the `libs` folder can be explicitly imported. This obviously doesn't scale
# well, but may be a necessary evil until ST2 upgrades its internal Python.
#
tmpdir = os.getcwdu()
os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__.encode('utf-8')), u'modules', u'libs')))

for mod in [u'capp_lint', u'pep8', u'pyflakes', u'pyflakes.api', u'pyflakes.checker', u'pyflakes.messages', u'pyflakes.reporter']:
    __import__(mod)
    print u'imported {0}'.format(mod)

os.chdir(tmpdir)


class Loader(object):
    '''utility class to load (and reload if necessary) SublimeLinter modules'''
    def __init__(self, basedir, linters):
        '''assign relevant variables and load all existing linter modules'''
        self.basedir = basedir
        self.basepath = u'sublimelinter/modules'
        self.linters = linters
        self.modpath = self.basepath.replace('/', u'.')
        self.ignored = ('__init__', 'base_linter')
        self.fix_path()
        self.load_all()

    def fix_path(self):
        if os.name != 'posix':
            return

        path = os.environ['PATH'].encode('utf-8')
        home_path = os.path.join(os.path.expanduser(u'~'), u'bin')

        if path:
            dirs = path.encode('utf-8').split(':')

            if u'/usr/local/bin' not in dirs:
                dirs.insert(0, u'/usr/local/bin')

            if home_path not in dirs:
                dirs.append(home_path)

            os.environ['PATH'] = u':'.join(dirs)

    def load_all(self):
        '''loads all existing linter modules'''
        for modf in glob.glob(u'{0}/*.py'.format(self.basepath)):
            base, name = os.path.split(modf)
            name = name.split('.', 1)[0]

            if name in self.ignored:
                continue

            self.load_module(name)

    def load_module(self, name):
        '''loads a single linter module'''
        fullmod = u'{0}.{1}'.format(self.modpath, name)

        # make sure the path didn't change on us (this is needed for submodule reload)
        pushd = os.getcwdu()
        os.chdir(self.basedir)

        __import__(fullmod)

        # this following line of code does two things:
        # first, we get the actual module from sys.modules,
        #    not the base mod returned by __import__
        # second, we get an updated version with reload()
        #    so module development is easier
        # (to make sublime text reload language submodule,
        #  just save sublimelinter_plugin.py )
        mod = sys.modules[fullmod] = reload(sys.modules[fullmod])

        # update module's __file__ to absolute path so we can reload it
        # if saved with sublime text
        mod.__file__ = os.path.abspath(mod.__file__.encode('utf-8')).rstrip('co')

        language = ''

        try:
            config = base_linter.CONFIG.copy()

            try:
                config.update(mod.CONFIG)
                language = config['language']
            except (AttributeError, KeyError):
                pass

            if language:
                if hasattr(mod, 'Linter'):
                    linter = mod.Linter(config)
                else:
                    linter = base_linter.BaseLinter(config)

                lc_language = language.lower()
                self.linters[lc_language] = linter
                print u'SublimeLinter: {0} loaded'.format(language)
            else:
                print u'SublimeLinter: {0} disabled (no language specified in module)'.format(name)

        except KeyError:
            print u'SublimeLinter: general error importing {0} ({1})'.format(name, language or '<unknown>')

        os.chdir(pushd)

    def reload_module(self, module):
        '''reload a single linter module
           This method is meant to be used when editing a given
           linter module so that changes can be viewed immediately
           upon saving without having to restart Sublime Text'''
        fullmod = module.__name__

        if not fullmod.startswith(self.modpath):
            return

        name = fullmod.replace(self.modpath + '.', '', 1)
        self.load_module(name)


================================================
FILE: sublimelinter/modules/__init__.py
================================================


================================================
FILE: sublimelinter/modules/base_linter.py
================================================
# base_linter.py - base class for linters

import os
import os.path
import json
import re
import subprocess

import sublime

# If the linter uses an executable that takes stdin, use this input method.
INPUT_METHOD_STDIN = 1

# If the linter uses an executable that does not take stdin but you wish to use
# a temp file so that the current view can be linted interactively, use this input method.
# If the current view has been saved, the tempfile will have the same name as the
# view's file, which is necessary for some linters.
INPUT_METHOD_TEMP_FILE = 2

# If the linter uses an executable that does not take stdin and you wish to have
# linting occur only on file load and save, use this input method.
INPUT_METHOD_FILE = 3

CONFIG = {
    # The display language name for this linter.
    'language': '',

    # Linters may either use built in code or use an external executable. This item may have
    # one of the following values:
    #
    #   string - An external command (or path to a command) to execute
    #   None - The linter is considered to be built in
    #
    # Alternately, your linter class may define the method get_executable(),
    # which should return the three-tuple (<enabled>, <executable>, <message>):
    #   <enabled> must be a boolean than indicates whether the executable is available and usable.
    #   If <enabled> is True, <executable> must be one of:
    #       - A command string (or path to a command) if an external executable will be used
    #       - None if built in code will be used
    #       - False if no suitable executable can be found or the linter should be disabled
    #         for some other reason.
    #   <message> is the message that will be shown in the console when the linter is
    #   loaded, to aid the user in knowing what the status of the linter is. If None or an empty string,
    #   a default message will be returned based on the value of <executable>. Otherwise it
    #   must be a string.
    'executable': None,

    # If an external executable is being used, this item specifies the arguments
    # used when checking the existence of the executable to determine if the linter can be enabled.
    # If more than one argument needs to be passed, use a tuple/list.
    # Defaults to '-v' if this item is missing.
    'test_existence_args': '-v',

    # If an external executable is being used, this item specifies the arguments to be passed
    # when linting. If there is more than one argument, use a tuple/list.
    # If the input method is anything other than INPUT_METHOD_STDIN, put a {filename} placeholder in
    # the args where the filename should go.
    #
    # Alternately, if your linter class may define the method get_lint_args(), which should return
    # None for no arguments or a tuple/list for one or more arguments.
    'lint_args': None,

    # If an external executable is being used, the method used to pass input to it. Defaults to STDIN.
    'input_method': INPUT_METHOD_STDIN
}

TEMPFILES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__.encode('utf-8')), u'..', u'.tempfiles'))

JSON_MULTILINE_COMMENT_RE = re.compile(r'\/\*[\s\S]*?\*\/')
JSON_SINGLELINE_COMMENT_RE = re.compile(r'\/\/[^\n\r]*')

if not os.path.exists(TEMPFILES_DIR):
    os.mkdir(TEMPFILES_DIR)


class BaseLinter(object):
    '''A base class for linters. Your linter module needs to do the following:

            - Set the relevant values in CONFIG
            - Override built_in_check() if it uses a built in linter. You may return
              whatever value you want, this value will be passed to parse_errors().
            - Override parse_errors() and populate the relevant lists/dicts. The errors
              argument passed to parse_errors() is the output of the executable run through strip().

       If you do subclass and override __init__, be sure to call super(MyLinter, self).__init__(config).
    '''

    JSC_PATH = '/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc'

    LIB_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__.encode('utf-8')), u'libs'))

    JAVASCRIPT_ENGINES = ['node', 'jsc']
    JAVASCRIPT_ENGINE_NAMES = {'node': 'node.js', 'jsc': 'JavaScriptCore'}
    JAVASCRIPT_ENGINE_WRAPPERS_PATH = os.path.join(LIB_PATH, 'jsengines')

    def __init__(self, config):
        self.language = config['language']
        self.enabled = False
        self.executable = config.get('executable', None)
        self.test_existence_args = config.get('test_existence_args', ['-v'])
        self.js_engine = None

        if isinstance(self.test_existence_args, basestring):
            self.test_existence_args = (self.test_existence_args,)

        self.input_method = config.get('input_method', INPUT_METHOD_STDIN)
        self.filename = None
        self.lint_args = config.get('lint_args', [])

        if isinstance(self.lint_args, basestring):
            self.lint_args = [self.lint_args]

    def check_enabled(self, view):
        if hasattr(self, 'get_executable'):
            try:
                self.enabled, self.executable, message = self.get_executable(view)

                if self.enabled and not message:
                    message = 'using "{0}"'.format(self.executable) if self.executable else 'built in'
            except Exception as ex:
                self.enabled = False
                message = unicode(ex)
        else:
            self.enabled, message = self._check_enabled(view)

        return (self.enabled, message or '<unknown reason>')

    def _check_enabled(self, view):
        if self.executable is None:
            return (True, 'built in')
        elif isinstance(self.executable, basestring):
            self.executable = self.get_mapped_executable(view, self.executable)
        elif isinstance(self.executable, bool) and self.executable is False:
            return (False, 'unknown error')
        else:
            return (False, 'bad type for CONFIG["executable"]')

        # If we get this far, the executable is external. Test that it can be executed
        # and capture stdout and stderr so they don't end up in the system log.
        try:
            args = [self.executable]
            args.extend(self.test_existence_args)
            subprocess.Popen(args, startupinfo=self.get_startupinfo(),
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()
        except OSError:
            return (False, '"{0}" cannot be found'.format(self.executable))

        return (True, 'using "{0}" for executable'.format(self.executable))

    def _get_lint_args(self, view, code, filename):
        if hasattr(self, 'get_lint_args'):
            return self.get_lint_args(view, code, filename) or []
        else:
            lintArgs = self.lint_args or []
            settings = view.settings().get('SublimeLinter', {}).get(self.language, {})

            if settings:
                if 'lint_args' in settings:
                    lintArgs = settings['lint_args']

                cwd = settings.get('working_directory', '').encode('utf-8')

                if cwd and os.path.isabs(cwd) and os.path.isdir(cwd):
                    os.chdir(cwd)

            return [arg.format(filename=filename) for arg in lintArgs]

    def built_in_check(self, view, code, filename):
        return ''

    def executable_check(self, view, code, filename):
        args = [self.executable]
        tempfilePath = None

        if self.input_method == INPUT_METHOD_STDIN:
            args.extend(self._get_lint_args(view, code, filename))

        elif self.input_method == INPUT_METHOD_TEMP_FILE:
            if filename:
                filename = os.path.basename(filename)
            else:
                filename = u'view{0}'.format(view.id())

            tempfilePath = os.path.join(TEMPFILES_DIR, filename)

            with open(tempfilePath, 'w') as f:
                f.write(code)

            args.extend(self._get_lint_args(view, code, tempfilePath))
            code = u''

        elif self.input_method == INPUT_METHOD_FILE:
            args.extend(self._get_lint_args(view, code, filename))
            code = u''

        else:
            return u''

        try:
            process = subprocess.Popen(args,
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT,
                                       startupinfo=self.get_startupinfo())
            process.stdin.write(code)
            result = process.communicate()[0]
        finally:
            if tempfilePath:
                os.remove(tempfilePath)

        return result.strip()

    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        pass

    def add_message(self, lineno, lines, message, messages):
        # Assume lineno is one-based, ST2 wants zero-based line numbers
        lineno -= 1
        lines.add(lineno)
        message = message[0].upper() + message[1:]

        # Remove trailing period from error message
        if message[-1] == '.':
            message = message[:-1]

        if lineno in messages:
            messages[lineno].append(message)
        else:
            messages[lineno] = [message]

    def underline_range(self, view, lineno, position, underlines, length=1):
        # Assume lineno is one-based, ST2 wants zero-based line numbers
        lineno -= 1
        line = view.full_line(view.text_point(lineno, 0))
        position += line.begin()

        for i in xrange(length):
            underlines.append(sublime.Region(position + i))

    def underline_regex(self, view, lineno, regex, lines, underlines, wordmatch=None, linematch=None):
        # Assume lineno is one-based, ST2 wants zero-based line numbers
        lineno -= 1
        lines.add(lineno)
        offset = 0
        line = view.full_line(view.text_point(lineno, 0))
        lineText = view.substr(line)

        if linematch:
            match = re.match(linematch, lineText)

            if match:
                lineText = match.group('match')
                offset = match.start('match')
            else:
                return

        iters = re.finditer(regex, lineText)
        results = [(result.start('underline'), result.end('underline')) for result in iters if not wordmatch or result.group('underline') == wordmatch]

        # Make the lineno one-based again for underline_range
        lineno += 1

        for start, end in results:
            self.underline_range(view, lineno, start + offset, underlines, end - start)

    def underline_word(self, view, lineno, position, underlines):
        # Assume lineno is one-based, ST2 wants zero-based line numbers
        lineno -= 1
        line = view.full_line(view.text_point(lineno, 0))
        position += line.begin()

        word = view.word(position)
        underlines.append(word)

    def run(self, view, code, filename=None):
        self.filename = filename

        if self.executable is None:
            errors = self.built_in_check(view, code, filename)
        else:
            errors = self.executable_check(view, code, filename)

        lines = set()
        errorUnderlines = []  # leave this here for compatibility with original plugin
        errorMessages = {}
        violationUnderlines = []
        violationMessages = {}
        warningUnderlines = []
        warningMessages = {}

        self.parse_errors(view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages)
        return lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages

    def get_mapped_executable(self, view, default):
        map = view.settings().get('sublimelinter_executable_map')

        if map:
            lang = self.language.lower()

            if lang in map:
                return map[lang].encode('utf-8')

        return default

    def get_startupinfo(self):
        info = None

        if os.name == 'nt':
            info = subprocess.STARTUPINFO()
            info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            info.wShowWindow = subprocess.SW_HIDE

        return info

    def execute_get_output(self, args):
        try:
            return subprocess.Popen(args, self.get_startupinfo()).communicate()[0]
        except:
            return ''

    def jsc_path(self):
        '''Return the path to JavaScriptCore. Use this method in case the path
           has to be dynamically calculated in the future.'''
        return self.JSC_PATH

    def find_file(self, filename, view):
        '''Find a file with the given name, starting in the view's directory,
           then ascending the file hierarchy up to root.'''
        path = (view.file_name() or '').encode('utf-8')

        # quit if the view is temporary
        if not path:
            return None

        dirname = os.path.dirname(path)

        while True:
            path = os.path.join(dirname, filename)

            if os.path.isfile(path):
                with open(path, 'r') as f:
                    return f.read()

            # if we hit root, quit
            parent = os.path.dirname(dirname)

            if parent == dirname:
                return None
            else:
                dirname = parent

    def strip_json_comments(self, json_str):
        stripped_json = JSON_MULTILINE_COMMENT_RE.sub('', json_str)
        stripped_json = JSON_SINGLELINE_COMMENT_RE.sub('', stripped_json)
        return json.dumps(json.loads(stripped_json))

    def get_javascript_args(self, view, linter, code):
        path = os.path.join(self.LIB_PATH, linter)
        options = self.get_javascript_options(view)

        if options is None:
            options = json.dumps(view.settings().get('%s_options' % linter) or {})

        self.get_javascript_engine(view)
        engine = self.js_engine

        if (engine['name'] == 'jsc'):
            args = [engine['wrapper'], '--', path + os.path.sep, str(code.count('\n')), options]
        else:
            args = [engine['wrapper'], path + os.path.sep, options]

        return args

    def get_javascript_options(self, view):
        '''Subclasses should override this if they want to provide options
           for a JavaScript-based linter. If the subclass cannot provide
           options, it should return None (or not return anything).'''
        return None

    def get_javascript_engine(self, view):
        if self.js_engine is None:
            for engine in self.JAVASCRIPT_ENGINES:
                if engine == 'node':
                    try:
                        path = self.get_mapped_executable(view, 'node')
                        subprocess.call([path, u'-v'], startupinfo=self.get_startupinfo())
                        self.js_engine = {
                            'name': engine,
                            'path': path,
                            'wrapper': os.path.join(self.JAVASCRIPT_ENGINE_WRAPPERS_PATH, engine + '.js'),
                        }
                        break
                    except OSError:
                        pass

                elif engine == 'jsc':
                    if os.path.exists(self.jsc_path()):
                        self.js_engine = {
                            'name': engine,
                            'path': self.jsc_path(),
                            'wrapper': os.path.join(self.JAVASCRIPT_ENGINE_WRAPPERS_PATH, engine + '.js'),
                        }
                        break

        if self.js_engine is not None:
            return (True, self.js_engine['path'], 'using {0}'.format(self.JAVASCRIPT_ENGINE_NAMES[self.js_engine['name']]))

        # Didn't find an engine, tell the user
        engine_list = ', '.join(self.JAVASCRIPT_ENGINE_NAMES.values())
        return (False, '', 'One of the following JavaScript engines must be installed: ' + engine_list)


================================================
FILE: sublimelinter/modules/c.py
================================================
import re

from base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE

CONFIG = {
    'language': 'C',
    'executable': 'cppcheck',
    'lint_args': ['--enable=style', '--quiet', '{filename}'],
    'input_method': INPUT_METHOD_TEMP_FILE
}


class Linter(BaseLinter):
    CPPCHECK_RE = re.compile(r'\[.+?:(\d+?)\](.+)')

    def __init__(self, config):
        super(Linter, self).__init__(config)

    def parse_errors(self, view, errors, lines, errorUnderlines,
                     violationUnderlines, warningUnderlines,
                     errorMessages, violationMessages,
                     warningMessages):
        # Go through each line in the output of cppcheck
        for line in errors.splitlines():
            match = self.CPPCHECK_RE.match(line)
            if match:
                # The regular expression matches the line number and
                # the message as its two groups.
                lineno, message = match.group(1), match.group(2)
                # Remove the colon at the beginning of the message
                if len(message) > 0 and message[0] == ':':
                    message = message[1:].strip()
                lineno = int(lineno)
                self.add_message(lineno, lines, message, errorMessages)


================================================
FILE: sublimelinter/modules/c_cpplint.py
================================================
import re

from base_linter import BaseLinter

CONFIG = {
    'language': 'c_cpplint',
    'executable': 'cpplint.py',
    'test_existence_args': ['--help'],
    'lint_args': '-',
}


class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        for line in errors.splitlines():
            match = re.match(r'^.+:(?P<line>\d+):\s+(?P<error>.+)', line)

            if match:
                error, line = match.group('error'), match.group('line')
                self.add_message(int(line), lines, error, errorMessages)


================================================
FILE: sublimelinter/modules/coffeescript.py
================================================
import re
import os

from base_linter import BaseLinter

CONFIG = {
    'language': 'CoffeeScript',
    'executable': 'coffee.cmd' if os.name == 'nt' else 'coffee',
    'lint_args': ['-s', '-l']
}


class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines,
                     violationUnderlines, warningUnderlines, errorMessages,
                     violationMessages, warningMessages):

        for line in errors.splitlines():
            match = re.match(r'.*?Error: Parse error on line '
                             r'(?P<line>\d+): (?P<error>.+)', line)
            if not match:
                match = re.match(r'.*?Error: (?P<error>.+) '
                                 r'on line (?P<line>\d+)', line)
            if not match:
                match = re.match(r'[^:]+:(?P<line>\d+):\d+: '
                                 r'error: (?P<error>.+)', line)

            if match:
                line, error = match.group('line'), match.group('error')
                self.add_message(int(line), lines, error, errorMessages)


================================================
FILE: sublimelinter/modules/css.py
================================================
import json

from base_linter import BaseLinter

CONFIG = {
    'language': 'CSS'
}


class Linter(BaseLinter):
    def __init__(self, config):
        super(Linter, self).__init__(config)

    def get_executable(self, view):
        return self.get_javascript_engine(view)

    def get_lint_args(self, view, code, filename):
        return self.get_javascript_args(view, 'csslint', code)

    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        try:
            errors = json.loads(errors.strip() or '[]')
        except ValueError:
            raise ValueError("Error from csslint: {0}".format(errors))

        for error in errors:
            lineno = error['line']

            if error['type'] == 'warning':
                messages = warningMessages
                underlines = warningUnderlines
            else:
                messages = errorMessages
                underlines = errorUnderlines

            self.add_message(lineno, lines, error['reason'], messages)
            self.underline_range(view, lineno, error['character'] - 1, underlines)


================================================
FILE: sublimelinter/modules/git_commit_message.py
================================================
from base_linter import BaseLinter


CONFIG = {
    'language': 'Git Commit Message'
}


class ErrorType:
    WARNING = 'warning'
    VIOLATION = 'violation'
    ERROR = 'error'


class Linter(BaseLinter):

    def built_in_check(self, view, code, filename):
        lines = code.splitlines()
        lineno = 0
        real_lineno = 0
        first_line_of_message = None
        first_line_of_body = None
        errors = []

        for line in lines:
            real_lineno += 1

            if line.startswith('#'):
                continue

            if line.startswith('diff --git'):
                break

            lineno += 1

            if first_line_of_message is None:
                if line.strip():
                    first_line_of_message = lineno

                    if len(line) > 68:
                        errors.append({
                            'type': ErrorType.ERROR,
                            'message': 'Subject line must be 68 characters or less (github will truncate).',
                            'lineno': real_lineno,
                            'col': 68,
                        })
                    elif len(line) > 50:
                        errors.append({
                            'type': ErrorType.WARNING,
                            'message': 'Subject line should be 50 characters or less.',
                            'lineno': real_lineno,
                            'col': 50,
                        })
                    elif lineno != 1:
                        errors.append({
                            'type': ErrorType.ERROR,
                            'message': 'Subject must be on first line.',
                            'lineno': real_lineno,
                        })
                    elif line[0].upper() != line[0]:
                        errors.append({
                            'type': ErrorType.VIOLATION,
                            'message': 'Subject line should be capitalized.',
                            'lineno': real_lineno,
                        })
            elif first_line_of_body is None:
                if len(line):
                    first_line_of_body = lineno

                    if lineno == first_line_of_message + 1:
                        if len(line):
                            errors.append({
                                'message': 'Leave a blank line between the message subject and body.',
                                'lineno': first_line_of_message + 1,
                            })
                    elif lineno > first_line_of_message + 2:
                        errors.append({
                            'message': 'Leave exactly 1 blank line between the message subject and body.',
                            'lineno': real_lineno,
                        })
            if first_line_of_body is not None:
                if len(line) > 72:
                    errors.append({
                        'message': 'Lines must not exceed 72 characters.',
                        'lineno': real_lineno,
                        'col': 72,
                    })

        return errors

    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        for error in errors:
            error_type = error.get('type', ErrorType.ERROR)
            col = error.get('col', 0)

            messages = {
                ErrorType.WARNING: warningMessages,
                ErrorType.VIOLATION: violationMessages,
                ErrorType.ERROR: errorMessages,
            }[error_type]
            underlines = {
                ErrorType.WARNING: warningUnderlines,
                ErrorType.VIOLATION: violationUnderlines,
                ErrorType.ERROR: errorUnderlines,
            }[error_type]

            self.add_message(error['lineno'], lines, error['message'], messages)
            self.underline_range(view, error['lineno'], col, underlines, length=1)


================================================
FILE: sublimelinter/modules/haml.py
================================================
import re

from base_linter import BaseLinter

CONFIG = {
    'language': 'Ruby Haml',
    'executable': 'haml',
    'lint_args': '-c'
}


class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        for line in errors.splitlines():
            match = re.match(r'^.+(?P<line>\d+):\s+(?P<error>.+)', line)

            if match:
                error, line = match.group('error'), match.group('line')
                self.add_message(int(line), lines, error, errorMessages)


================================================
FILE: sublimelinter/modules/haskell.py
================================================
import re
from base_linter import BaseLinter, INPUT_METHOD_FILE


CONFIG = {
    'language': 'haskell',
    'executable': 'hlint',
    'input_method': INPUT_METHOD_FILE,
    'lint_args': '{filename}'
}


class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        i = 0
        error_lines = errors.splitlines()
        while i < len(error_lines):
            error = re.match(r'^.+:(?P<line>\d+):(?P<col>\d+): (?P<error>.+)', error_lines[i])

            if error:
                message, lineno, col = error.group('error'), int(error.group('line')), int(error.group('col'))

                if error_lines[i + 1] == "Error message:":
                    message = error_lines[i + 2]
                    i += 2

                lint_error = re.match(r'^(?P<type>Error|Warning): (?P<error>.+)', message)

                if lint_error:
                    error_type, message = lint_error.group('type'), lint_error.group('error')

                    if error_type == 'Warning':
                        messages = warningMessages
                        underlines = warningUnderlines
                    else:
                        messages = errorMessages
                        underlines = errorUnderlines

                self.add_message(lineno, lines, message, messages)
                self.underline_range(view, lineno, col - 1, underlines)

            i += 1


================================================
FILE: sublimelinter/modules/html.py
================================================
# Example error messages
#
# line 1 column 1 - Warning: missing <!DOCTYPE> declaration
# line 200 column 1 - Warning: discarding unexpected </div>
# line 1 column 1 - Warning: inserting missing 'title' element

import re
import subprocess

from base_linter import BaseLinter

CONFIG = {
    'language': 'HTML',
    'executable': 'tidy',
    'lint_args': '-eq'
}


class Linter(BaseLinter):
    def get_executable(self, view):
        try:
            path = self.get_mapped_executable(view, 'tidy')
            version_string = subprocess.Popen([path, '-v'], startupinfo=self.get_startupinfo(), stdout=subprocess.PIPE).communicate()[0]

            if u'HTML5' in version_string:
                return (True, path, 'using tidy for executable')

            return (False, '', 'tidy is not ready for HTML5')
        except OSError:
            return (False, '', 'tidy cannot be found')

    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        for line in errors.splitlines():
            match = re.match(r'^line\s(?P<line>\d+)\scolumn\s\d+\s-\s(?P<error>.+)', line)

            if match:
                error, line = match.group('error'), match.group('line')
                self.add_message(int(line), lines, error, errorMessages)


================================================
FILE: sublimelinter/modules/java.py
================================================
import os
import os.path
import re

from base_linter import BaseLinter, INPUT_METHOD_FILE

CONFIG = {
    'language': 'Java',
    'executable': 'javac',
    'test_existence_args': '-version',
    'input_method': INPUT_METHOD_FILE
}

ERROR_RE = re.compile(r'^(?P<path>.*\.java):(?P<line>\d+): (?P<warning>warning: )?(?:\[\w+\] )?(?P<error>.*)')
MARK_RE = re.compile(r'^(?P<mark>\s*)\^$')


class Linter(BaseLinter):
    def parse_errors(self, view, errors, lines, errorUnderlines,
                     violationUnderlines, warningUnderlines, errorMessages,
                     violationMessages, warningMessages):
        it = iter(errors.splitlines())

        for line in it:
            match = re.match(ERROR_RE, line)

            if match:
                path = os.path.abspath(match.group('path'))

                if path != self.filename:
                    continue

                lineNumber = int(match.group('line'))
                warning = match.group('warning')
                error = match.group('error')

                if warning:
                    messages = warningMessages
                    underlines = warningUnderlines
                else:
                    messages = errorMessages
                    underlines = errorUnderlines

                # Skip forward until we find the marker
                position = -1

                while True:
                    line = it.next()
                    match = re.match(MARK_RE, line)

                    if match:
                        position = len(match.group('mark'))
                        break

                self.add_message(lineNumber, lines, error, messages)
                self.underline_range(view, lineNumber, position, underlines)


================================================
FILE: sublimelinter/modules/javascript.py
================================================
import json
import re
import subprocess

from base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE

CONFIG = {
    'language': 'JavaScript'
}


class Linter(BaseLinter):
    GJSLINT_RE = re.compile(r'Line (?P<line>\d+),\s*E:(?P<errnum>\d+):\s*(?P<message>.+)')

    def __init__(self, config):
        super(Linter, self).__init__(config)
        self.linter = None

    def get_executable(self, view):
        self.linter = view.settings().get('javascript_linter', 'jshint')

        if (self.linter in ('jshint', 'jslint')):
            return self.get_javascript_engine(view)
        elif (self.linter == 'gjslint'):
            try:
                path = self.get_mapped_executable(view, 'gjslint')
                subprocess.call([path, u'--help'], startupinfo=self.get_startupinfo())
                self.input_method = INPUT_METHOD_TEMP_FILE
                return (True, path, 'using gjslint')
            except OSError:
                return (False, '', 'gjslint cannot be found')
        else:
            return (False, '', '"{0}" is not a valid javascript linter'.format(self.linter))

    def get_lint_args(self, view, code, filename):
        if (self.linter == 'gjslint'):
            args = []
            gjslint_options = view.settings().get("gjslint_options", [])
            args.extend(gjslint_options)
            args.extend([u'--nobeep', filename])
            return args
        elif (self.linter in ('jshint', 'jslint')):
            return self.get_javascript_args(view, self.linter, code)
        else:
            return []

    def get_javascript_options(self, view):
        if self.linter == 'jshint':
            rc_options = self.find_file('.jshintrc', view)

            if rc_options is not None:
                rc_options = self.strip_json_comments(rc_options)
                return json.dumps(json.loads(rc_options))

    def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
        if (self.linter == 'gjslint'):
            ignore = view.settings().get('gjslint_ignore', [])

            for line in errors.splitlines():
                match = self.GJSLINT_RE.match(line)

                if match:
                    line, errnum, message = match.group('line'), match.group('errnum'), match.group('message')

                    if (int(errnum) not in ignore):
                        self.add_message(int(line), lines, message, errorMessages)

        elif (self.linter in ('jshint', 'jslint')):
            try:
                errors = json.loads(errors.strip() or '[]')
            except ValueError:
                raise ValueError("Error from {0}: {1}".format(self.linter, errors))

            for error in errors:
                lineno = error['line']
                self.add_message(lineno, lines, error['reason'], errorMessages)
                self.underline_range(view, lineno, error['character'] - 1, errorUnderlines)


================================================
FILE: sublimelinter/modules/libs/capp_lint.py
================================================
#!/usr/bin/env python
#
# capp_lint.py - Check Objective-J source code formatting,
# according to Cappuccino standards:
#
# http://cappuccino.org/contribute/coding-style.php
#
# Copyright (C) 2011 Aparajita Fishman <aparajita@aparajita.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 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.

from __future__ import with_statement
from optparse import OptionParser
from string import Template
import cgi
import cStringIO
import os
import os.path
import re
import sys
import unittest

EXIT_CODE_SHOW_HTML = 205
EXIT_CODE_SHOW_TOOLTIP = 206


def exit_show_html(html):
    sys.stdout.write(html.encode('utf-8'))
    sys.exit(EXIT_CODE_SHOW_HTML)


def exit_show_tooltip(text):
    sys.stdout.write(text)
    sys.exit(EXIT_CODE_SHOW_TOOLTIP)


def within_textmate():
    return os.getenv('TM_APP_PATH') is not None


def tabs2spaces(text, positions=None):
    while True:
        index = text.find(u'\t')

        if index < 0:
            return text

        spaces = u' ' * (4 - (index % 4))
        text = text[0:index] + spaces + text[index + 1:]

        if positions is not None:
            positions.append(index)


def relative_path(basedir, filename):
    if filename.find(basedir) == 0:
        filename = filename[len(basedir) + 1:]

    return filename


def string_replacer(line):
    """Take string literals like 'hello' and replace them with empty string literals, while respecting escaping."""

    r = []
    in_quote = None
    escapes = 0
    for i, c in enumerate(line):
        if in_quote:
            if not escapes and c == in_quote:
                in_quote = None
                r.append(c)
                continue

            # We're inside of a string literal. Ignore everything.
        else:
            if not escapes and (c == '"' or c == "'"):
                in_quote = c
                r.append(c)
                continue

            # Outside of a string literal, preserve everything.
            r.append(c)

        if c == '\\':
            escapes = (escapes + 1) % 2
        else:
            escapes = 0

    if in_quote:
        # Unterminated string literal.
        pass
    return "".join(r)


class LintChecker(object):
    """Examine Objective-J code statically and generate warnings for possible errors and deviations from the coding-style standard.

    >>> LintChecker().lint_text('var b = 5+5;')
    [{'positions': [9], 'filename': '<stdin>', 'lineNum': 1, 'message': 'binary operator without surrounding spaces', 'type': 2, 'line': u'var b = 5+5;'}]

    >>> LintChecker().lint_text('''
    ... if( 1 ) {
    ...   var b=7;
    ...       c = 8;
    ... }
    ... ''')
    [{'positions': [2], 'filename': '<stdin>', 'lineNum': 2, 'message': 'missing space between control statement and parentheses', 'type': 2, 'line': u'if( 1 ) {'}, {'positions': [8], 'filename': '<stdin>', 'lineNum': 2, 'message': 'braces should be on their own line', 'type': 1, 'line': u'if( 1 ) {'}, {'positions': [3, 5], 'filename': '<stdin>', 'lineNum': 2, 'message': 'space inside parentheses', 'type': 1, 'line': u'if( 1 ) {'}, {'positions': [7], 'filename': '<stdin>', 'lineNum': 3, 'message': 'assignment operator without surrounding spaces', 'type': 2, 'line': u'  var b=7;'}, {'lineNum': 4, 'message': 'accidental global variable', 'type': 1, 'line': u'      c = 8;', 'filename': '<stdin>'}]
    """

    VAR_BLOCK_START_RE = re.compile(ur'''(?x)
        (?P<indent>\s*)         # indent before a var keyword
        (?P<var>var\s+)         # var keyword and whitespace after
        (?P<identifier>[a-zA-Z_$]\w*)\s*
        (?:
            (?P<assignment>=)\s*
            (?P<expression>.*)
            |
            (?P<separator>[,;+\-/*%^&|=\\])
        )
    ''')

    SEPARATOR_RE = re.compile(ur'''(?x)
        (?P<expression>.*)              # Everything up to the line separator
        (?P<separator>[,;+\-/*%^&|=\\]) # The line separator
        \s*                             # Optional whitespace after
        $                               # End of expression
    ''')

    INDENTED_EXPRESSION_RE_TEMPLATE = ur'''(?x)
        [ ]{%d}                 # Placeholder for indent of first identifier that started block
        (?P<expression>.+)      # Expression
    '''

    VAR_BLOCK_RE_TEMPLATE = ur'''(?x)
        [ ]{%d}                 # Placeholder for indent of first identifier that started block
        (?P<indent>\s*)         # Capture any further indent
        (?:
            (?P<bracket>[\[\{].*)
            |
            (?P<identifier>[a-zA-Z_$]\w*)\s*
            (?:
                (?P<assignment>=)\s*
                (?P<expression>.*)
                |
                (?P<separator>[,;+\-/*%%^&|=\\])
            )
            |
            (?P<indented_expression>.+)
        )
    '''

    STATEMENT_RE = re.compile(ur'''(?x)
        \s*((continue|do|for|function|if|else|return|switch|while|with)\b|\[+\s*[a-zA-Z_$]\w*\s+[a-zA-Z_$]\w*\s*[:\]])
    ''')

    TRAILING_WHITESPACE_RE = re.compile(ur'^.*(\s+)$')
    STRIP_LINE_COMMENT_RE = re.compile(ur'(.*)\s*(?://.*|/\*.*\*/\s*)$')
    LINE_COMMENT_RE = re.compile(ur'\s*(?:/\*.*\*/\s*|//.*)$')
    COMMENT_RE = re.compile(ur'/\*.*?\*/')
    BLOCK_COMMENT_START_RE = re.compile(ur'\s*/\*.*(?!\*/\s*)$')
    BLOCK_COMMENT_END_RE = re.compile(ur'.*?\*/')
    METHOD_RE = ur'[-+]\s*\([a-zA-Z_$]\w*\)\s*[a-zA-Z_$]\w*'
    FUNCTION_RE = re.compile(ur'\s*function\s*(?P<name>[a-zA-Z_$]\w*)?\(.*\)\s*\{?')
    RE_RE = re.compile(ur'(?<!\\)/.*?[^\\]/[gims]*')
    EMPTY_STRING_LITERAL_FUNCTION = lambda match: match.group(1) + (len(match.group(2)) * ' ') + match.group(1)
    EMPTY_SELF_STRING_LITERAL_FUNCTION = lambda self, match: match.group(1) + (len(match.group(2)) * ' ') + match.group(1)

    def noncapturing(regex):
        return ur'(?:%s)' % regex

    def optional(regex):
        return ur'(?:%s)?' % regex

    DECIMAL_DIGIT_RE = ur'[0-9]'
    NON_ZERO_DIGIT_RE = ur'[1-9]'
    DECIMAL_DIGITS_RE = DECIMAL_DIGIT_RE + ur'+'
    DECIMAL_DIGITS_OPT_RE = optional(DECIMAL_DIGIT_RE + ur'+')
    EXPONENT_INDICATOR_RE = ur'[eE]'
    SIGNED_INTEGER_RE = noncapturing(DECIMAL_DIGITS_RE) + ur'|' + noncapturing(ur'\+' + DECIMAL_DIGITS_RE) + ur'|' + noncapturing('-' + DECIMAL_DIGITS_RE)
    DECIMAL_INTEGER_LITERAL_RE = ur'0|' + noncapturing(NON_ZERO_DIGIT_RE + DECIMAL_DIGIT_RE + ur'*')
    EXPONENT_PART_RE = EXPONENT_INDICATOR_RE + noncapturing(SIGNED_INTEGER_RE)
    EXPONENT_PART_OPT_RE = optional(EXPONENT_PART_RE)

    DECIMAL_LITERAL_RE = re.compile(noncapturing(noncapturing(DECIMAL_INTEGER_LITERAL_RE) + ur'\.' + DECIMAL_DIGITS_OPT_RE + EXPONENT_PART_OPT_RE) + ur'|\.' + noncapturing(DECIMAL_DIGITS_RE + EXPONENT_PART_OPT_RE) + ur'|' + noncapturing(noncapturing(DECIMAL_INTEGER_LITERAL_RE) + EXPONENT_PART_OPT_RE))

    ERROR_TYPE_ILLEGAL = 1
    ERROR_TYPE_WARNING = 2

    # Replace the contents of comments, regex and string literals
    # with spaces so we don't get false matches within them
    STD_IGNORES = (
        {'regex': STRIP_LINE_COMMENT_RE, 'replace': ''},
        {'function': string_replacer},
        {'regex': COMMENT_RE, 'replace': ''},
        {'regex': RE_RE, 'replace': '/ /'},
    )

    # Convert exponential notation like 1.1e-6 to an arbitrary constant number so that the "e" notation doesn't
    # need to be understood by the regular matchers. Obviously this is limited by the fact that we're regexing
    # so this will probably catch some things which are not properly decimal literals (parts of strings or
    # variable names for instance).
    EXPONENTIAL_TO_SIMPLE = (
        {'regex': DECIMAL_LITERAL_RE, 'replace': '42'},
    )

    LINE_CHECKLIST = (
        {
            'id': 'tabs',
            'regex': re.compile(ur'[\t]'),
            'error': 'line contains tabs',
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            'regex': re.compile(ur'([^\t -~])'),
            'error': 'line contains non-ASCII characters',
            'showPositionForGroup': 1,
            'type': ERROR_TYPE_ILLEGAL,
            'option': 'sublimelinter_objj_check_ascii',
            'optionDefault': False
        },
        {
            'regex': re.compile(ur'^\s*(?:(?:else )?if|for|switch|while|with)(\()'),
            'error': 'missing space between control statement and parentheses',
            'showPositionForGroup': 1,
            'type': ERROR_TYPE_WARNING
        },
        {
            'regex': re.compile(ur'^\s*(?:(?:else )?if|for|switch|while|with)\s*\(.+\)\s*(\{)\s*(?://.*|/\*.*\*/\s*)?$'),
            'error': 'braces should be on their own line',
            'showPositionForGroup': 1,
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            'regex': re.compile(ur'^\s*(?:(?:else )?if|for|switch|while|with)\s*\((\s+)?.+?(\s+)?\)\s*(?:(?:\{|//.*|/\*.*\*/)\s*)?$'),
            'error': 'space inside parentheses',
            'showPositionForGroup': [1, 2],
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            'regex': re.compile(ur'^\s*(?:(?:else )?if|for|switch|while|with)\s*\(.+\)\s*(?:[\w_]|\[).+(;)\s*(?://.*|/\*.*\*/\s*)?$'),
            'error': 'dependent statements must be on their own line',
            'showPositionForGroup': 1,
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            'regex': TRAILING_WHITESPACE_RE,
            'error': 'trailing whitespace',
            'showPositionForGroup': 1,
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            # Filter out @import statements, method declarations, method parameters, unary plus/minus/increment/decrement
            'filter': {'regex': re.compile(ur'(^@import\b|^\s*' + METHOD_RE + '|^\s*[a-zA-Z_$]\w*:\s*\([a-zA-Z_$][\w<>]*\)\s*\w+|[a-zA-Z_$]\w*(\+\+|--)|([ -+*/%^&|<>!]=?|&&|\|\||<<|>>>|={1,3}|!==?)\s*[-+][\w(\[])'), 'pass': False},

            # Also convert literals like 1.5e+7 to 42 so that the - or + in there is ignored for purposes of this warning.
            'preprocess': STD_IGNORES + EXPONENTIAL_TO_SIMPLE,
            'regex': re.compile(ur'(?<=[\w)\]"\']|([ ]))([-+*/%^]|&&?|\|\|?|<<|>>>?)(?=[\w({\["\']|(?(1)\b\b|[ ]))'),
            'error': 'binary operator without surrounding spaces',
            'showPositionForGroup': 2,
            'type': ERROR_TYPE_WARNING
        },
        {
            # Filter out possible = within @accessors
            'filter': {'regex': re.compile(ur'^\s*(?:@outlet\s+)?[a-zA-Z_$]\w*\s+[a-zA-Z_$]\w*\s+@accessors\b'), 'pass': False},

            'preprocess': STD_IGNORES,
            'regex': re.compile(ur'(?<=[\w)\]"\']|([ ]))(=|[-+*/%^&|]=|<<=|>>>?=)(?=[\w({\["\']|(?(1)\b\b|[ ]))'),
            'error': 'assignment operator without surrounding spaces',
            'showPositionForGroup': 2,
            'type': ERROR_TYPE_WARNING
        },
        {
            # Filter out @import statements and @implementation/method declarations
            'filter': {'regex': re.compile(ur'^(@import\b|@implementation\b|\s*' + METHOD_RE + ')'), 'pass': False},

            'preprocess': STD_IGNORES,
            'regex': re.compile(ur'(?<=[\w)\]"\']|([ ]))(===?|!==?|[<>]=?)(?=[\w({\["\']|(?(1)\b\b|[ ]))'),
            'error': 'comparison operator without surrounding spaces',
            'showPositionForGroup': 2,
            'type': ERROR_TYPE_WARNING
        },
        {
            'regex': re.compile(ur'^(\s+)' + METHOD_RE + '|^\s*[-+](\()[a-zA-Z_$][\w]*\)\s*[a-zA-Z_$]\w*|^\s*[-+]\s*\([a-zA-Z_$][\w]*\)(\s+)[a-zA-Z_$]\w*'),
            'error': 'extra or missing space in a method declaration',
            'showPositionForGroup': 0,
            'type': ERROR_TYPE_WARNING
        },
        {
            # Check for brace following a class or method declaration
            'regex': re.compile(ur'^(?:\s*[-+]\s*\([a-zA-Z_$]\w*\)|@implementation)\s*[a-zA-Z_$][\w]*.*?\s*(\{)\s*(?:$|//.*$)'),
            'error': 'braces should be on their own line',
            'showPositionForGroup': 0,
            'type': ERROR_TYPE_ILLEGAL
        },
        {
            'regex': re.compile(ur'^\s*var\s+[a-zA-Z_$]\w*\s*=\s*function\s+([a-zA-Z_$]\w*)\s*\('),
            'error': 'function name is ignored',
            'showPositionForGroup': 1,
            'skip': True,
            'type': ERROR_TYPE_WARNING
        },
    )

    VAR_DECLARATIONS = ['none', 'single', 'strict']
    VAR_DECLARATIONS_NONE = 0
    VAR_DECLARATIONS_SINGLE = 1
    VAR_DECLARATIONS_STRICT = 2

    DIRS_TO_SKIP = ('.git', 'Frameworks', 'Build', 'Resources', 'CommonJS', 'Objective-J')

    ERROR_FORMATS = ('text', 'html')
    TEXT_ERROR_SINGLE_FILE_TEMPLATE = Template(u'$lineNum: $message.\n+$line\n')
    TEXT_ERROR_MULTI_FILE_TEMPLATE = Template(u'$filename:$lineNum: $message.\n+$line\n')

    def __init__(self, view=None, basedir='', var_declarations=VAR_DECLARATIONS_SINGLE, verbose=False):
        self.view = view
        self.basedir = unicode(basedir, 'utf-8')
        self.errors = []
        self.errorFiles = []
        self.filesToCheck = []
        self.varDeclarations = var_declarations
        self.verbose = verbose
        self.sourcefile = None
        self.filename = u''
        self.line = u''
        self.lineNum = 0
        self.varIndent = u''
        self.identifierIndent = u''

        self.fileChecklist = (
            {'title': 'Check variable blocks', 'action': self.check_var_blocks},
        )

    def run_line_checks(self):
        for check in self.LINE_CHECKLIST:
            option = check.get('option')

            if option:
                default = check.get('optionDefault', False)

                if self.view and not self.view.settings().get(option, default):
                    continue

            line = self.line
            originalLine = line
            lineFilter = check.get('filter')

            if lineFilter:
                match = lineFilter['regex'].search(line)

                if (match and not lineFilter['pass']) or (not match and lineFilter['pass']):
                    continue

            preprocess = check.get('preprocess')

            if preprocess:
                if not isinstance(preprocess, (list, tuple)):
                    preprocess = (preprocess,)

                for processor in preprocess:
                    regex = processor.get('regex')

                    if regex:
                        line = regex.sub(processor.get('replace', ''), line)

                    fnct = processor.get('function')

                    if fnct:
                        line = fnct(line)

            regex = check.get('regex')

            if not regex:
                continue

            match = regex.search(line)

            if not match:
                continue

            positions = []
            groups = check.get('showPositionForGroup')

            if (check.get('id') == 'tabs'):
                line = tabs2spaces(line, positions=positions)
            elif groups is not None:
                line = tabs2spaces(line)

                if not isinstance(groups, (list, tuple)):
                    groups = (groups,)

                for match in regex.finditer(line):
                    for group in groups:
                        if group > 0:
                            start = match.start(group)

                            if start >= 0:
                                positions.append(start)
                        else:
                            # group 0 means show the first non-empty match
                            for i in range(1, len(match.groups()) + 1):
                                if match.start(i) >= 0:
                                    positions.append(match.start(i))
                                    break

            if positions:
                self.error(check['error'], line=originalLine, positions=positions, type=check['type'])

    def next_statement(self, expect_line=False, check_line=True):
        try:
            while True:
                raw_line = self.sourcefile.next()
                # strip EOL
                if raw_line[-1] == '\n':  # ... unless this is the last line which might not have a \n.
                    raw_line = raw_line[:-1]

                try:
                    self.line = unicode(raw_line, 'utf-8', 'strict')  # convert to Unicode
                    self.lineNum += 1
                except UnicodeDecodeError:
                    self.line = unicode(raw_line, 'utf-8', 'replace')
                    self.lineNum += 1
                    self.error('line contains invalid unicode character(s)', type=self.ERROR_TYPE_ILLEGAL)

                if self.verbose:
                    print u'%d: %s' % (self.lineNum, tabs2spaces(self.line))

                if check_line:
                    self.run_line_checks()

                if not self.is_statement():
                    continue

                return True
        except StopIteration:
            if expect_line:
                self.error('unexpected EOF', type=self.ERROR_TYPE_ILLEGAL)
            raise

    def is_statement(self):
        # Skip empty lines
        if len(self.line.strip()) == 0:
            return False

        # See if we have a line comment, skip that
        match = self.LINE_COMMENT_RE.match(self.line)

        if match:
            return False

        # Match a block comment start next so we can find its end,
        # otherwise we might get false matches on the contents of the block comment.
        match = self.BLOCK_COMMENT_START_RE.match(self.line)

        if match:
            self.block_comment()
            return False

        return True

    def is_expression(self):
        match = self.STATEMENT_RE.match(self.line)
        return match is None

    def strip_comment(self):
        match = self.STRIP_LINE_COMMENT_RE.match(self.expression)

        if match:
            self.expression = match.group(1)

    def get_expression(self, lineMatch):
        groupdict = lineMatch.groupdict()

        self.expression = groupdict.get('expression')

        if self.expression is None:
            self.expression = groupdict.get('bracket')

        if self.expression is None:
            self.expression = groupdict.get('indented_expression')

        if self.expression is None:
            self.expression = ''
            return

        # Remove all quoted strings from the expression so that we don't
        # count unmatched pairs inside the strings.
        self.expression = string_replacer(self.expression)

        self.strip_comment()
        self.expression = self.expression.strip()

    def block_comment(self):
        'Find the end of a block comment'

        commentOpenCount = self.line.count('/*')
        commentOpenCount -= self.line.count('*/')

        # If there is an open comment block, eat it
        if commentOpenCount:
            if self.verbose:
                print u'%d: BLOCK COMMENT START' % self.lineNum
        else:
            return

        match = None

        while not match and self.next_statement(expect_line=True, check_line=False):
            match = self.BLOCK_COMMENT_END_RE.match(self.line)

        if self.verbose:
            print u'%d: BLOCK COMMENT END' % self.lineNum

    def balance_pairs(self, squareOpenCount, curlyOpenCount, parenOpenCount):
        # The following lines have to be indented at least as much as the first identifier
        # after the var keyword at the start of the block.
        if self.verbose:
            print "%d: BALANCE BRACKETS: '['=%d, '{'=%d, '('=%d" % (self.lineNum, squareOpenCount, curlyOpenCount, parenOpenCount)

        lineRE = re.compile(self.INDENTED_EXPRESSION_RE_TEMPLATE % len(self.identifierIndent))

        while True:
            # If the expression has open brackets and is terminated, it's an error
            match = self.SEPARATOR_RE.match(self.expression)

            if match and match.group('separator') == ';':
                unterminated = []

                if squareOpenCount:
                    unterminated.append('[')

                if curlyOpenCount:
                    unterminated.append('{')

                if parenOpenCount:
                    unterminated.append('(')

                self.error('unbalanced %s' % ' and '.join(unterminated), type=self.ERROR_TYPE_ILLEGAL)
                return False

            self.next_statement(expect_line=True)
            match = lineRE.match(self.line)

            if not match:
                # If it doesn't match, the indent is wrong check the whole line
                self.error('incorrect indentation')
                self.expression = self.line
                self.strip_comment()
            else:
                # It matches, extract the expression
                self.get_expression(match)

            # Update the bracket counts
            squareOpenCount += self.expression.count('[')
            squareOpenCount -= self.expression.count(']')
            curlyOpenCount += self.expression.count('{')
            curlyOpenCount -= self.expression.count('}')
            parenOpenCount += self.expression.count('(')
            parenOpenCount -= self.expression.count(')')

            if squareOpenCount == 0 and curlyOpenCount == 0 and parenOpenCount == 0:
                if self.verbose:
                    print u'%d: BRACKETS BALANCED' % self.lineNum

                # The brackets are closed, this line must be separated
                match = self.SEPARATOR_RE.match(self.expression)

                if not match:
                    self.error('missing statement separator', type=self.ERROR_TYPE_ILLEGAL)
                    return False

                return True

    def pairs_balanced(self, lineMatchOrBlockMatch):

        groups = lineMatchOrBlockMatch.groupdict()

        if 'assignment' in groups or 'bracket' in groups:
            squareOpenCount = self.expression.count('[')
            squareOpenCount -= self.expression.count(']')

            curlyOpenCount = self.expression.count('{')
            curlyOpenCount -= self.expression.count('}')

            parenOpenCount = self.expression.count('(')
            parenOpenCount -= self.expression.count(')')

            if squareOpenCount or curlyOpenCount or parenOpenCount:
                # If the brackets were not properly closed or the statement was
                # missing a separator, skip the rest of the var block.
                if not self.balance_pairs(squareOpenCount, curlyOpenCount, parenOpenCount):
                    return False

        return True

    def var_block(self, blockMatch):
        """
        Parse a var block, return a tuple (haveLine, isSingleVar), where haveLine
        indicates whether self.line is the next line to be parsed.
        """

        # Keep track of whether this var block has multiple declarations
        isSingleVar = True

        # Keep track of the indent of the var keyword to compare with following lines
        self.varIndent = blockMatch.group('indent')

        # Keep track of how far the first variable name is indented to make sure
        # following lines line up with that
        self.identifierIndent = self.varIndent + blockMatch.group('var')

        # Check the expression to see if we have any open [ or { or /*
        self.get_expression(blockMatch)

        if not self.pairs_balanced(blockMatch):
            return (False, False)

        separator = ''

        if self.expression:
            match = self.SEPARATOR_RE.match(self.expression)

            if not match:
                self.error('missing statement separator', type=self.ERROR_TYPE_ILLEGAL)
            else:
                separator = match.group('separator')
        elif blockMatch.group('separator'):
            separator = blockMatch.group('separator')

        # If the block has a semicolon, there should be no more lines in the block
        blockHasSemicolon = separator == ';'

        # We may not catch an error till after the line that is wrong, so keep
        # the most recent declaration and its line number.
        lastBlockLine = self.line
        lastBlockLineNum = self.lineNum

        # Now construct an RE that will match any lines indented at least as much
        # as the var keyword that started the block.
        blockRE = re.compile(self.VAR_BLOCK_RE_TEMPLATE % len(self.identifierIndent))

        while self.next_statement(expect_line=not blockHasSemicolon):

            if not self.is_statement():
                continue

            # Is the line indented at least as much as the var keyword that started the block?
            match = blockRE.match(self.line)

            if match:
                if self.is_expression():
                    lastBlockLine = self.line
                    lastBlockLineNum = self.lineNum

                    # If the line is indented farther than the first identifier in the block,
                    # it is considered a formatting error.
                    if match.group('indent') and not match.group('indented_expression'):
                        self.error('incorrect indentation')

                    self.get_expression(match)

                    if not self.pairs_balanced(match):
                        return (False, isSingleVar)

                    if self.expression:
                        separatorMatch = self.SEPARATOR_RE.match(self.expression)

                        if separatorMatch is None:
                            # If the assignment does not have a separator, it's an error
                            self.error('missing statement separator', type=self.ERROR_TYPE_ILLEGAL)
                        else:
                            separator = separatorMatch.group('separator')

                            if blockHasSemicolon:
                                # If the block already has a semicolon, we have an accidental global declaration
                                self.error('accidental global variable', type=self.ERROR_TYPE_ILLEGAL)
                            elif (separator == ';'):
                                blockHasSemicolon = True
                    elif match.group('separator'):
                        separator = match.group('separator')

                    isSingleVar = False
                else:
                    # If the line is a control statement of some kind, then it should not be indented this far.
                    self.error('statement should be outdented from preceding var block')
                    return (True, False)

            else:
                # If the line does not match, it is not an assignment or is outdented from the block.
                # In either case, the block is considered closed. If the most recent separator was not ';',
                # the block was not properly terminated.
                if separator != ';':
                    self.error('unterminated var block', lineNum=lastBlockLineNum, line=lastBlockLine, type=self.ERROR_TYPE_ILLEGAL)

                return (True, isSingleVar)

    def check_var_blocks(self):
        lastStatementWasVar = False
        lastVarWasSingle = False
        haveLine = True

        while True:
            if not haveLine:
                haveLine = self.next_statement()

            if not self.is_statement():
                haveLine = False
                continue

            match = self.VAR_BLOCK_START_RE.match(self.line)

            if match is None:
                lastStatementWasVar = False
                haveLine = False
                continue

            # It might be a function definition, in which case we continue
            expression = match.group('expression')

            if expression:
                functionMatch = self.FUNCTION_RE.match(expression)

                if functionMatch:
                    lastStatementWasVar = False
                    haveLine = False
                    continue

            # Now we have the start of a variable block
            if self.verbose:
                print u'%d: VAR BLOCK' % self.lineNum

            varLineNum = self.lineNum
            varLine = self.line

            haveLine, isSingleVar = self.var_block(match)

            if self.verbose:
                print u'%d: END VAR BLOCK:' % self.lineNum,

                if isSingleVar:
                    print u'SINGLE'
                else:
                    print u'MULTIPLE'

            if lastStatementWasVar and self.varDeclarations != self.VAR_DECLARATIONS_NONE:
                if (self.varDeclarations == self.VAR_DECLARATIONS_SINGLE and lastVarWasSingle and isSingleVar) or \
                   (self.varDeclarations == self.VAR_DECLARATIONS_STRICT and (lastVarWasSingle or isSingleVar)):
                    self.error('consecutive var declarations', lineNum=varLineNum, line=varLine)

            lastStatementWasVar = True
            lastVarWasSingle = isSingleVar

    def run_file_checks(self):
        for check in self.fileChecklist:
            self.sourcefile.seek(0)
            self.lineNum = 0

            if self.verbose:
                print u'%s: %s' % (check['title'], self.sourcefile.name)

            check['action']()

    def lint(self, filesToCheck):
        # Recursively walk any directories and eliminate duplicates
        self.filesToCheck = []

        for filename in filesToCheck:
            filename = unicode(filename, 'utf-8')
            fullpath = os.path.join(self.basedir, filename)

            if fullpath not in self.filesToCheck:
                if os.path.isdir(fullpath):
                    for root, dirs, files in os.walk(fullpath):
                        for skipDir in self.DIRS_TO_SKIP:
                            if skipDir in dirs:
                                dirs.remove(skipDir)

                        for filename in files:
                            if not filename.endswith('.j'):
                                continue

                            fullpath = os.path.join(root, filename)

                            if fullpath not in self.filesToCheck:
                                self.filesToCheck.append(fullpath)
                else:
                    self.filesToCheck.append(fullpath)

        for filename in self.filesToCheck:
            try:
                with open(filename) as self.sourcefile:
                    self.filename = relative_path(self.basedir, filename)
                    self.run_file_checks()

            except IOError:
                self.lineNum = 0
                self.line = None
                self.error('file not found', type=self.ERROR_TYPE_ILLEGAL)

            except StopIteration:
                if self.verbose:
                    print u'EOF\n'
                pass

    def lint_text(self, text, filename="<stdin>"):
        self.filename = filename
        self.filesToCheck = []

        try:
            self.sourcefile = cStringIO.StringIO(text)
            self.run_file_checks()
        except StopIteration:
            if self.verbose:
                print u'EOF\n'
            pass

        return self.errors

    def count_files_checked(self):
        return len(self.filesToCheck)

    def error(self, message, **kwargs):
        info = {
            'filename':  self.filename,
            'message':   message,
            'type':      kwargs.get('type', self.ERROR_TYPE_WARNING)
        }

        line = kwargs.get('line', self.line)
        lineNum = kwargs.get('lineNum', self.lineNum)

        if line and lineNum:
            info['line'] = tabs2spaces(line)
            info['lineNum'] = lineNum

        positions = kwargs.get('positions')

        if positions:
            info['positions'] = positions

        self.errors.append(info)

        if self.filename not in self.errorFiles:
            self.errorFiles.append(self.filename)

    def has_errors(self):
        return len(self.errors) != 0

    def print_errors(self, format='text'):
        if not self.errors:
            return

        if format == 'text':
            self.print_text_errors()
        elif format == 'html':
            self.print_textmate_html_errors()
        elif format == 'tooltip':
            self.print_tooltip_errors()

    def print_text_errors(self):
        sys.stdout.write('%d error' % len(self.errors))

        if len(self.errors) > 1:
            sys.stdout.write('s')

        if len(self.filesToCheck) == 1:
            template = self.TEXT_ERROR_SINGLE_FILE_TEMPLATE
        else:
            sys.stdout.write(' in %d files' % len(self.errorFiles))
            template = self.TEXT_ERROR_MULTI_FILE_TEMPLATE

        sys.stdout.write(':\n\n')

        for error in self.errors:
            if 'lineNum' in error and 'line' in error:
                sys.stdout.write(template.substitute(error).encode('utf-8'))

                if error.get('positions'):
                    markers = ' ' * len(error['line'])

                    for position in error['positions']:
                        markers = markers[:position] + '^' + markers[position + 1:]

                    # Add a space at the beginning of the markers to account for the '+' at the beginning
                    # of the source line.
                    sys.stdout.write(' %s\n' % markers)
            else:
                sys.stdout.write('%s: %s.\n' % (error['filename'], error['message']))

            sys.stdout.write('\n')

    def print_textmate_html_errors(self):
        html = """
<html>
    <head>
        <title>Cappuccino Lint Report</title>
        <style type="text/css">
            body {
                margin: 0px;
                padding: 1px;
            }

            h1 {
                font: bold 12pt "Lucida Grande";
                color: #333;
                background-color: #FF7880;
                margin: 0 0 .5em 0;
                padding: .25em .5em;
            }

            p, a {
                margin: 0px;
                padding: 0px;
            }

            p {
                font: normal 10pt "Lucida Grande";
                color: #000;
            }

            p.error {
                background-color: #E2EAFF;
            }

            p.source {
                font-family: Consolas, 'Bitstream Vera Sans Mono', Monoco, Courier, sans-serif;
                white-space: pre;
                background-color: #fff;
                padding-bottom: 1em;
            }

            a {
                display: block;
                padding: .25em .5em;
                text-decoration: none;
                color: inherit;
                background-color: inherit;
            }

            a:hover {
                background-color: #ddd;
            }

            em {
                font-weight: normal;
                font-style: normal;
                font-variant: normal;
                background-color: #FF7880;
            }
        </style>
    </head>
    <body>
    """

        html += '<h1>Results: %d error' % len(self.errors)

        if len(self.errors) > 1:
            html += 's'

        if len(self.filesToCheck) > 1:
            html += ' in %d files' % len(self.errorFiles)

        html += '</h1>'

        for error in self.errors:
            message = cgi.escape(error['message'])

            if len(self.filesToCheck) > 1:
                filename = cgi.escape(error['filename']) + ':'
            else:
                filename = ''

            html += '<p class="error">'

            if 'line' in error and 'lineNum' in error:
                filepath = cgi.escape(os.path.join(self.basedir, error['filename']))
                lineNum = error['lineNum']
                line = error['line']
                positions = error.get('positions')
                firstPos = -1
                source = ''

                if positions:
                    firstPos = positions[0] + 1
                    lastPos = 0

                    for pos in error.get('positions'):
                        if pos < len(line):
                            charToHighlight = line[pos]
                        else:
                            charToHighlight = ''

                        source += '%s<em>%s</em>' % (cgi.escape(line[lastPos:pos]), cgi.escape(charToHighlight))
                        lastPos = pos + 1

                    if lastPos <= len(line):
                        source += cgi.escape(line[lastPos:])
                else:
                    source = line

                link = '<a href="txmt://open/?url=file://%s&line=%d&column=%d">' % (filepath, lineNum, firstPos)

                if len(self.filesToCheck) > 1:
                    errorMsg = '%s%d: %s' % (filename, lineNum, message)
                else:
                    errorMsg = '%d: %s' % (lineNum, message)

                html += '%(link)s%(errorMsg)s</a></p>\n<p class="source">%(link)s%(source)s</a></p>\n' % {'link': link, 'errorMsg': errorMsg, 'source': source}
            else:
                html += '%s%s</p>\n' % (filename, message)

        html += """
    </body>
</html>
"""
        exit_show_html(html)


class MiscTest(unittest.TestCase):
    def test_string_replacer(self):
        self.assertEquals(string_replacer("x = 'hello';"), "x = '';")
        self.assertEquals(string_replacer("x = '\\' hello';"), "x = '';")
        self.assertEquals(string_replacer("x = '\\\\';"), "x = '';")
        self.assertEquals(string_replacer("""x = '"string in string"';"""), "x = '';")

        self.assertEquals(string_replacer('x = "hello";'), 'x = "";')
        self.assertEquals(string_replacer('x = "\\" hello";'), 'x = "";')
        self.assertEquals(string_replacer('x = "\\\\";'), 'x = "";')
        self.assertEquals(string_replacer('''x = "'";'''), 'x = "";')


class LintCheckerTest(unittest.TestCase):
    def test_exponential_notation(self):
        """Test that exponential notation such as 1.1e-6 doesn't cause a warning about missing whitespace."""

        # This should not report "binary operator without surrounding spaces".
        self.assertEquals(LintChecker().lint_text("a = 2.1e-6;"), [])
        self.assertEquals(LintChecker().lint_text("a = 2.1e+6;"), [])
        self.assertEquals(LintChecker().lint_text("a = 2e-0;"), [])
        self.assertEquals(LintChecker().lint_text("a = 2e+0;"), [])

        # But this should.
        self.assertEquals(LintChecker().lint_text("a = 1.1e-6+2e2;"), [{'positions': [6], 'filename': '<stdin>', 'lineNum': 1, 'message': 'binary operator without surrounding spaces', 'type': 2, 'line': u'a = 1.1e-6+2e2;'}])

    def test_function_types(self):
        """Test that function definitions like function(/*CPString*/key) don't cause warnings about surrounding spaces."""

        # This should not report "binary operator without surrounding spaces".
        self.assertEquals(LintChecker().lint_text("var resolveMultipleValues = function(/*CPString*/key, /*CPDictionary*/bindings, /*GSBindingOperationKind*/operation)"), [])

    def test_unary_plus(self):
        """Test that = +<variable>, like in `x = +y;`, doesn't cause a warning."""

        # +<variable> converts number in a string to a number.
        self.assertEquals(LintChecker().lint_text("var y = +x;"), [])

    def test_string_escaping(self):
        """Test that string literals are not parsed as syntax, even when they end with a double backslash."""

        self.assertEquals(LintChecker().lint_text('var x = "(\\\\";'), [])


if __name__ == '__main__':
    usage = 'usage: %prog [options] [file ... | -]'
    parser = OptionParser(usage=usage, version='1.02')
    parser.add_option('-f', '--format', action='store', type='string', dest='format', default='text
Download .txt
gitextract__zefwhfl/

├── .codeintel/
│   └── config
├── .gitignore
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── LICENSE
├── Main.sublime-menu
├── README.md
├── SublimeLinter.py
├── SublimeLinter.sublime-settings
├── changelog.txt
├── gutter_mark_themes/
│   └── sublime-linter-gutter-markers.psd
├── messages/
│   ├── 1.5.1.txt
│   ├── 1.5.2.txt
│   ├── 1.5.3.txt
│   ├── 1.5.4.txt
│   ├── 1.5.5.txt
│   ├── 1.5.6.txt
│   ├── 1.5.7.txt
│   ├── 1.6.0.txt
│   ├── 1.6.1.txt
│   ├── 1.6.10.txt
│   ├── 1.6.11.txt
│   ├── 1.6.12.txt
│   ├── 1.6.13.txt
│   ├── 1.6.2.txt
│   ├── 1.6.3.txt
│   ├── 1.6.4.txt
│   ├── 1.6.5.txt
│   ├── 1.6.6.txt
│   ├── 1.6.7.txt
│   ├── 1.6.8.txt
│   ├── 1.6.9.txt
│   ├── 1.7.0.txt
│   ├── 1.7.1.txt
│   ├── 1.7.2.txt
│   ├── SublimeLinter3-update1.txt
│   ├── SublimeLinter3-update2.txt
│   ├── SublimeLinter3-update3.txt
│   ├── SublimeLinter3.txt
│   └── install.txt
├── messages.json
├── package_control.json
└── sublimelinter/
    ├── __init__.py
    ├── loader.py
    └── modules/
        ├── __init__.py
        ├── base_linter.py
        ├── c.py
        ├── c_cpplint.py
        ├── coffeescript.py
        ├── css.py
        ├── git_commit_message.py
        ├── haml.py
        ├── haskell.py
        ├── html.py
        ├── java.py
        ├── javascript.py
        ├── libs/
        │   ├── capp_lint.py
        │   ├── csslint/
        │   │   ├── csslint-node.js
        │   │   └── linter.js
        │   ├── jsengines/
        │   │   ├── jsc.js
        │   │   └── node.js
        │   ├── jshint/
        │   │   ├── jshint.js
        │   │   └── linter.js
        │   ├── jslint/
        │   │   ├── jslint.js
        │   │   └── linter.js
        │   ├── pep8.py
        │   └── pyflakes/
        │       ├── __init__.py
        │       ├── __main__.py
        │       ├── api.py
        │       ├── checker.py
        │       ├── messages.py
        │       └── reporter.py
        ├── lua.py
        ├── notes.py
        ├── objective-j.py
        ├── perl.py
        ├── php.py
        ├── puppet-lint.py
        ├── puppet.py
        ├── python.py
        ├── ruby-lint.py
        ├── ruby.py
        ├── squirrel.py
        ├── sublime_pylint.py
        └── xml.py
Download .txt
SYMBOL INDEX (674 symbols across 36 files)

FILE: SublimeLinter.py
  function get_delay (line 83) | def get_delay(t, view):
  function last_selected_lineno (line 104) | def last_selected_lineno(view):
  function update_statusbar (line 111) | def update_statusbar(view):
  function run_once (line 132) | def run_once(linter, view, **kwargs):
  function popup_error_list (line 162) | def popup_error_list(view):
  function add_lint_marks (line 213) | def add_lint_marks(view, lines, error_underlines, violation_underlines, ...
  function erase_lint_marks (line 275) | def erase_lint_marks(view):
  function get_lint_regions (line 286) | def get_lint_regions(view, reverse=False, coalesce=False):
  function select_lint_region (line 330) | def select_lint_region(view, region):
  function find_underline_within (line 345) | def find_underline_within(view, region):
  function syntax_name (line 358) | def syntax_name(view):
  function select_linter (line 364) | def select_linter(view, ignore_disabled=False):
  function highlight_notes (line 402) | def highlight_notes(view):
  function _update_view (line 412) | def _update_view(view, filename, **kwargs):
  function queue_linter (line 436) | def queue_linter(linter, view, timeout=-1, preemptive=False, event=None):
  function _callback (line 456) | def _callback(view, filename, kwargs):
  function background_linter (line 460) | def background_linter():
  function queue_loop (line 480) | def queue_loop():
  function queue (line 495) | def queue(view, callback, kwargs):
  function _delay_queue (line 519) | def _delay_queue(timeout, preemptive):
  function delay_queue (line 550) | def delay_queue(timeout):
  function queue_finalize (line 571) | def queue_finalize(timeout=None):
  function view_in_tab (line 596) | def view_in_tab(view, title, text, file_type):
  function lint_views (line 612) | def lint_views(linter):
  function reload_view_module (line 629) | def reload_view_module(view):
  function settings_changed (line 640) | def settings_changed():
  function reload_settings (line 649) | def reload_settings(view):
  class LintCommand (line 663) | class LintCommand(sublime_plugin.TextCommand):
    method __init__ (line 666) | def __init__(self, view):
    method run_ (line 670) | def run_(self, action):
    method reset (line 694) | def reset(self):
    method on (line 699) | def on(self):
    method enable_load_save (line 704) | def enable_load_save(self):
    method enable_save_only (line 709) | def enable_save_only(self):
    method off (line 714) | def off(self):
    method _run (line 719) | def _run(self, name):
  class BackgroundLinter (line 724) | class BackgroundLinter(sublime_plugin.EventListener):
    method __init__ (line 730) | def __init__(self):
    method on_modified (line 734) | def on_modified(self, view):
    method on_load (line 754) | def on_load(self, view):
    method on_post_save (line 764) | def on_post_save(self, view):
    method on_selection_modified (line 776) | def on_selection_modified(self, view):
  class FindLintErrorCommand (line 790) | class FindLintErrorCommand(sublime_plugin.TextCommand):
    method is_enabled (line 792) | def is_enabled(self):
    method find_lint_error (line 795) | def find_lint_error(self, forward):
  class FindNextLintErrorCommand (line 840) | class FindNextLintErrorCommand(FindLintErrorCommand):
    method run (line 841) | def run(self, edit):
  class FindPreviousLintErrorCommand (line 850) | class FindPreviousLintErrorCommand(FindLintErrorCommand):
    method run (line 851) | def run(self, edit):
  class SublimelinterWindowCommand (line 860) | class SublimelinterWindowCommand(sublime_plugin.WindowCommand):
    method is_enabled (line 861) | def is_enabled(self):
    method run_ (line 872) | def run_(self, args):
  class SublimelinterAnnotationsCommand (line 876) | class SublimelinterAnnotationsCommand(SublimelinterWindowCommand):
    method run_ (line 880) | def run_(self, args):
  class SublimelinterCommand (line 898) | class SublimelinterCommand(SublimelinterWindowCommand):
    method is_enabled (line 899) | def is_enabled(self):
    method run_ (line 908) | def run_(self, args={}):
    method lint_view (line 918) | def lint_view(self, view, show_popup_list):
  class SublimelinterLintCommand (line 937) | class SublimelinterLintCommand(SublimelinterCommand):
    method is_enabled (line 938) | def is_enabled(self):
  class SublimelinterShowErrorsCommand (line 950) | class SublimelinterShowErrorsCommand(SublimelinterCommand):
    method is_enabled (line 951) | def is_enabled(self):
  class SublimelinterEnableLoadSaveCommand (line 955) | class SublimelinterEnableLoadSaveCommand(SublimelinterCommand):
    method is_enabled (line 956) | def is_enabled(self):
  class SublimelinterEnableSaveOnlyCommand (line 968) | class SublimelinterEnableSaveOnlyCommand(SublimelinterCommand):
    method is_enabled (line 969) | def is_enabled(self):
  class SublimelinterDisableCommand (line 981) | class SublimelinterDisableCommand(SublimelinterCommand):
    method is_enabled (line 982) | def is_enabled(self):

FILE: sublimelinter/loader.py
  class Loader (line 34) | class Loader(object):
    method __init__ (line 36) | def __init__(self, basedir, linters):
    method fix_path (line 46) | def fix_path(self):
    method load_all (line 64) | def load_all(self):
    method load_module (line 75) | def load_module(self, name):
    method reload_module (line 126) | def reload_module(self, module):

FILE: sublimelinter/modules/base_linter.py
  class BaseLinter (line 76) | class BaseLinter(object):
    method __init__ (line 96) | def __init__(self, config):
    method check_enabled (line 113) | def check_enabled(self, view):
    method _check_enabled (line 128) | def _check_enabled(self, view):
    method _get_lint_args (line 150) | def _get_lint_args(self, view, code, filename):
    method built_in_check (line 168) | def built_in_check(self, view, code, filename):
    method executable_check (line 171) | def executable_check(self, view, code, filename):
    method parse_errors (line 213) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...
    method add_message (line 216) | def add_message(self, lineno, lines, message, messages):
    method underline_range (line 231) | def underline_range(self, view, lineno, position, underlines, length=1):
    method underline_regex (line 240) | def underline_regex(self, view, lineno, regex, lines, underlines, word...
    method underline_word (line 266) | def underline_word(self, view, lineno, position, underlines):
    method run (line 275) | def run(self, view, code, filename=None):
    method get_mapped_executable (line 294) | def get_mapped_executable(self, view, default):
    method get_startupinfo (line 305) | def get_startupinfo(self):
    method execute_get_output (line 315) | def execute_get_output(self, args):
    method jsc_path (line 321) | def jsc_path(self):
    method find_file (line 326) | def find_file(self, filename, view):
    method strip_json_comments (line 352) | def strip_json_comments(self, json_str):
    method get_javascript_args (line 357) | def get_javascript_args(self, view, linter, code):
    method get_javascript_options (line 374) | def get_javascript_options(self, view):
    method get_javascript_engine (line 380) | def get_javascript_engine(self, view):

FILE: sublimelinter/modules/c.py
  class Linter (line 13) | class Linter(BaseLinter):
    method __init__ (line 16) | def __init__(self, config):
    method parse_errors (line 19) | def parse_errors(self, view, errors, lines, errorUnderlines,

FILE: sublimelinter/modules/c_cpplint.py
  class Linter (line 13) | class Linter(BaseLinter):
    method parse_errors (line 14) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/coffeescript.py
  class Linter (line 13) | class Linter(BaseLinter):
    method parse_errors (line 14) | def parse_errors(self, view, errors, lines, errorUnderlines,

FILE: sublimelinter/modules/css.py
  class Linter (line 10) | class Linter(BaseLinter):
    method __init__ (line 11) | def __init__(self, config):
    method get_executable (line 14) | def get_executable(self, view):
    method get_lint_args (line 17) | def get_lint_args(self, view, code, filename):
    method parse_errors (line 20) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/git_commit_message.py
  class ErrorType (line 9) | class ErrorType:
  class Linter (line 15) | class Linter(BaseLinter):
    method built_in_check (line 17) | def built_in_check(self, view, code, filename):
    method parse_errors (line 91) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/haml.py
  class Linter (line 12) | class Linter(BaseLinter):
    method parse_errors (line 13) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/haskell.py
  class Linter (line 13) | class Linter(BaseLinter):
    method parse_errors (line 14) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/html.py
  class Linter (line 19) | class Linter(BaseLinter):
    method get_executable (line 20) | def get_executable(self, view):
    method parse_errors (line 32) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/java.py
  class Linter (line 18) | class Linter(BaseLinter):
    method parse_errors (line 19) | def parse_errors(self, view, errors, lines, errorUnderlines,

FILE: sublimelinter/modules/javascript.py
  class Linter (line 12) | class Linter(BaseLinter):
    method __init__ (line 15) | def __init__(self, config):
    method get_executable (line 19) | def get_executable(self, view):
    method get_lint_args (line 35) | def get_lint_args(self, view, code, filename):
    method get_javascript_options (line 47) | def get_javascript_options(self, view):
    method parse_errors (line 55) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/libs/capp_lint.py
  function exit_show_html (line 45) | def exit_show_html(html):
  function exit_show_tooltip (line 50) | def exit_show_tooltip(text):
  function within_textmate (line 55) | def within_textmate():
  function tabs2spaces (line 59) | def tabs2spaces(text, positions=None):
  function relative_path (line 73) | def relative_path(basedir, filename):
  function string_replacer (line 80) | def string_replacer(line):
  class LintChecker (line 114) | class LintChecker(object):
    method noncapturing (line 187) | def noncapturing(regex):
    method optional (line 190) | def optional(regex):
    method __init__ (line 334) | def __init__(self, view=None, basedir='', var_declarations=VAR_DECLARA...
    method run_line_checks (line 353) | def run_line_checks(self):
    method next_statement (line 428) | def next_statement(self, expect_line=False, check_line=True):
    method is_statement (line 459) | def is_statement(self):
    method is_expression (line 480) | def is_expression(self):
    method strip_comment (line 484) | def strip_comment(self):
    method get_expression (line 490) | def get_expression(self, lineMatch):
    method block_comment (line 512) | def block_comment(self):
    method balance_pairs (line 533) | def balance_pairs(self, squareOpenCount, curlyOpenCount, parenOpenCount):
    method pairs_balanced (line 593) | def pairs_balanced(self, lineMatchOrBlockMatch):
    method var_block (line 615) | def var_block(self, blockMatch):
    method check_var_blocks (line 716) | def check_var_blocks(self):
    method run_file_checks (line 772) | def run_file_checks(self):
    method lint (line 782) | def lint(self, filesToCheck):
    method lint_text (line 824) | def lint_text(self, text, filename="<stdin>"):
    method count_files_checked (line 838) | def count_files_checked(self):
    method error (line 841) | def error(self, message, **kwargs):
    method has_errors (line 865) | def has_errors(self):
    method print_errors (line 868) | def print_errors(self, format='text'):
    method print_text_errors (line 879) | def print_text_errors(self):
    method print_textmate_html_errors (line 911) | def print_textmate_html_errors(self):
  class MiscTest (line 1038) | class MiscTest(unittest.TestCase):
    method test_string_replacer (line 1039) | def test_string_replacer(self):
  class LintCheckerTest (line 1051) | class LintCheckerTest(unittest.TestCase):
    method test_exponential_notation (line 1052) | def test_exponential_notation(self):
    method test_function_types (line 1064) | def test_function_types(self):
    method test_unary_plus (line 1070) | def test_unary_plus(self):
    method test_string_escaping (line 1076) | def test_string_escaping(self):

FILE: sublimelinter/modules/libs/csslint/csslint-node.js
  function EventTarget (line 59) | function EventTarget(){
  function StringReader (line 147) | function StringReader(text){
  function SyntaxError (line 416) | function SyntaxError(message, line, col){
  function SyntaxUnit (line 452) | function SyntaxUnit(text, line, col, type){
  function TokenStreamBase (line 531) | function TokenStreamBase(input, tokenData){
  function Combinator (line 1149) | function Combinator(text, line, col){
  function MediaFeature (line 1187) | function MediaFeature(name, value){
  function MediaQuery (line 1223) | function MediaQuery(modifier, mediaType, features, line, col){
  function Parser (line 1270) | function Parser(options){
  function PropertyName (line 3984) | function PropertyName(text, hack, line, col){
  function PropertyValue (line 4016) | function PropertyValue(parts, line, col){
  function PropertyValueIterator (line 4042) | function PropertyValueIterator(value){
  function PropertyValuePart (line 4171) | function PropertyValuePart(text, line, col){
  function Selector (line 4357) | function Selector(parts, line, col){
  function SelectorPart (line 4398) | function SelectorPart(elementName, modifiers, text, line, col){
  function SelectorSubPart (line 4437) | function SelectorSubPart(text, type, line, col){
  function Specificity (line 4472) | function Specificity(a, b, c, d){
  function updateValues (line 4536) | function updateValues(part){
  function isHexDigit (line 4596) | function isHexDigit(c){
  function isDigit (line 4600) | function isDigit(c){
  function isWhitespace (line 4604) | function isWhitespace(c){
  function isNewLine (line 4608) | function isNewLine(c){
  function isNameStart (line 4612) | function isNameStart(c){
  function isNameChar (line 4616) | function isNameChar(c){
  function isIdentStart (line 4620) | function isIdentStart(c){
  function mix (line 4624) | function mix(receiver, supplier){
  function TokenStream (line 4645) | function TokenStream(input){
  function ValidationError (line 5970) | function ValidationError(message, line, col){
  function applyEmbeddedRuleset (line 6453) | function applyEmbeddedRuleset(text, ruleset){
  function Reporter (line 6627) | function Reporter(lines, ruleset){
  function startRule (line 6920) | function startRule(){
  function endRule (line 6925) | function endRule(){
  function reportProperty (line 7305) | function reportProperty(name, display, msg){
  function startRule (line 7313) | function startRule(){
  function endRule (line 7317) | function endRule(){
  function startRule (line 7439) | function startRule(event){
  function startRule (line 7554) | function startRule(event){
  function startRule (line 7923) | function startRule(event){
  function endRule (line 7937) | function endRule(event){
  function startRule (line 8249) | function startRule(event){
  function endRule (line 8254) | function endRule(event){
  function startRule (line 8340) | function startRule(event){
  function endRule (line 8346) | function endRule(event){
  function startRule (line 8634) | function startRule(){
  function endRule (line 8640) | function endRule(event){

FILE: sublimelinter/modules/libs/jsengines/node.js
  function run (line 12) | function run() {

FILE: sublimelinter/modules/libs/jshint/jshint.js
  function i (line 5) | function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&re...
  function indexOf (line 69) | function indexOf (xs, x) {
  function checkOption (line 1308) | function checkOption(name, t) {
  function isString (line 1325) | function isString(obj) {
  function isIdentifier (line 1329) | function isIdentifier(tkn, value) {
  function isReserved (line 1339) | function isReserved(token) {
  function supplant (line 1367) | function supplant(str, data) {
  function combine (line 1374) | function combine(t, o) {
  function updatePredefined (line 1383) | function updatePredefined() {
  function assume (line 1389) | function assume() {
  function quit (line 1487) | function quit(code, line, chr) {
  function isundef (line 1501) | function isundef(scope, code, token, a) {
  function warning (line 1505) | function warning(code, t, a, b, c, d) {
  function warningAt (line 1556) | function warningAt(m, l, ch, a, b, c, d) {
  function error (line 1563) | function error(m, t, a, b, c, d) {
  function errorAt (line 1567) | function errorAt(m, l, ch, a, b, c, d) {
  function addInternalSrc (line 1575) | function addInternalSrc(elem, src) {
  function addlabel (line 1586) | function addlabel(t, type, tkn, islet) {
  function doOption (line 1646) | function doOption() {
  function peek (line 1857) | function peek(p) {
  function advance (line 1872) | function advance(id, t) {
  function isInfix (line 1934) | function isInfix(token) {
  function isEndOfExpr (line 1938) | function isEndOfExpr() {
  function expression (line 1964) | function expression(rbp, initial) {
  function adjacent (line 2048) | function adjacent(left, right) {
  function nobreak (line 2059) | function nobreak(left, right) {
  function nospace (line 2067) | function nospace(left, right) {
  function nonadjacent (line 2077) | function nonadjacent(left, right) {
  function nobreaknonadjacent (line 2093) | function nobreaknonadjacent(left, right) {
  function indentation (line 2108) | function indentation(bias) {
  function nolinebreak (line 2123) | function nolinebreak(t) {
  function nobreakcomma (line 2130) | function nobreakcomma(left, right) {
  function comma (line 2145) | function comma(opts) {
  function symbol (line 2212) | function symbol(s, p) {
  function delim (line 2224) | function delim(s) {
  function stmt (line 2228) | function stmt(s, f) {
  function blockstmt (line 2235) | function blockstmt(s, f) {
  function reserveName (line 2241) | function reserveName(x) {
  function prefix (line 2249) | function prefix(s, f) {
  function type (line 2268) | function type(s, f) {
  function reserve (line 2275) | function reserve(name, func) {
  function FutureReservedWord (line 2282) | function FutureReservedWord(name, meta) {
  function reservevar (line 2298) | function reservevar(s, v) {
  function infix (line 2307) | function infix(s, f, p, w) {
  function application (line 2331) | function application(s) {
  function relation (line 2349) | function relation(s, f) {
  function isPoorRelation (line 2382) | function isPoorRelation(node) {
  function assignop (line 2392) | function assignop(s, f, p) {
  function bitwise (line 2453) | function bitwise(s, f, p) {
  function bitwiseassignop (line 2468) | function bitwiseassignop(s) {
  function suffix (line 2491) | function suffix(s) {
  function optionalidentifier (line 2511) | function optionalidentifier(fnparam, prop) {
  function identifier (line 2549) | function identifier(fnparam, prop) {
  function reachable (line 2562) | function reachable(s) {
  function statement (line 2590) | function statement(noindent) {
  function statements (line 2689) | function statements(startLine) {
  function directives (line 2714) | function directives() {
  function block (line 2777) | function block(ordinary, stmt, isfunc, isfatarrow) {
  function countMember (line 2901) | function countMember(m) {
  function note_implied (line 2913) | function note_implied(tkn) {
  function comprehensiveArrayExpression (line 3563) | function comprehensiveArrayExpression() {
  function property_name (line 3642) | function property_name() {
  function functionparams (line 3663) | function functionparams(parsed) {
  function doFunction (line 3761) | function doFunction(name, statement, generator, fatarrowparams) {
  function createMetrics (line 3823) | function createMetrics(functionStartToken) {
  function increaseComplexityCount (line 3861) | function increaseComplexityCount() {
  function checkCondAssignment (line 3868) | function checkCondAssignment(expr) {
  function saveProperty (line 3901) | function saveProperty(name, tkn) {
  function saveSetter (line 3911) | function saveSetter(name, tkn) {
  function saveGetter (line 3923) | function saveGetter(name) {
  function destructuringExpression (line 4082) | function destructuringExpression() {
  function destructuringExpressionMatch (line 4135) | function destructuringExpressionMatch(tokens, value) {
  function classdef (line 4379) | function classdef(stmt) {
  function classtail (line 4396) | function classtail(c) {
  function doCatch (line 4490) | function doCatch() {
  function destructuringAssignOrJsonValue (line 5161) | function destructuringAssignOrJsonValue() {
  function declare (line 5195) | function declare(v) {
  function use (line 5205) | function use(v) {
  function jsonValue (line 5273) | function jsonValue() {
  function _checkBlockLabels (line 5365) | function _checkBlockLabels() {
  function each (line 5438) | function each(obj, cb) {
  method isJSON (line 5521) | get isJSON() {
  function asyncTrigger (line 6274) | function asyncTrigger() {
  function Lexer (line 6318) | function Lexer(source) {
  function commentToken (line 6596) | function commentToken(label, body, opt) {
  function isUnicodeLetter (line 6761) | function isUnicodeLetter(code) {
  function isHexDigit (line 6775) | function isHexDigit(str) {
  function isDecimalDigit (line 6909) | function isDecimalDigit(str) {
  function isOctalDigit (line 6913) | function isOctalDigit(str) {
  function isHexDigit (line 6917) | function isHexDigit(str) {
  function isIdentifierStart (line 6921) | function isIdentifierStart(ch) {
  function isReserved (line 7605) | function isReserved(token, isProperty) {
  function log (line 7852) | function log() {}
  function info (line 7854) | function info() {
  function warn (line 7858) | function warn() {
  function error (line 7862) | function error() {
  function time (line 7866) | function time(label) {
  function timeEnd (line 7870) | function timeEnd(label) {
  function trace (line 7880) | function trace() {
  function dir (line 7887) | function dir(object) {
  function assert (line 7891) | function assert(expression) {
  function format (line 9182) | function format(value, recurseTimes) {
  function isArray (line 9357) | function isArray(ar) {
  function isRegExp (line 9364) | function isRegExp(re) {
  function isDate (line 9370) | function isDate(d) {
  function pad (line 9378) | function pad(n) {
  function timestamp (line 9386) | function timestamp() {
  function objectKeys (line 9487) | function objectKeys(object) {
  function replacer (line 9523) | function replacer(key, value) {
  function truncate (line 9536) | function truncate(s, n) {
  function fail (line 9572) | function fail(actual, expected, message, operator, stackStartFunction) {
  function ok (line 9592) | function ok(value, message) {
  function _deepEqual (line 9623) | function _deepEqual(actual, expected) {
  function isUndefinedOrNull (line 9658) | function isUndefinedOrNull(value) {
  function isArguments (line 9662) | function isArguments(object) {
  function objEquiv (line 9666) | function objEquiv(a, b) {
  function expectedException (line 9736) | function expectedException(actual, expected) {
  function _throws (line 9752) | function _throws(shouldThrow, block, expected, message) {
  function SlowBuffer (line 9885) | function SlowBuffer (size) {
  function toHex (line 9894) | function toHex(n) {
  function utf8ToBytes (line 9899) | function utf8ToBytes(str) {
  function asciiToBytes (line 9913) | function asciiToBytes(str) {
  function base64ToBytes (line 9922) | function base64ToBytes(str) {
  function blitBuffer (line 9947) | function blitBuffer(src, dst, offset, length) {
  function decodeUtf8Char (line 9981) | function decodeUtf8Char(str) {
  function coerce (line 10205) | function coerce(length) {
  function Buffer (line 10216) | function Buffer(subject, encoding, offset) {
  function isArrayIsh (line 10279) | function isArrayIsh(subject) {
  function allocPool (line 10291) | function allocPool() {
  function readUInt16 (line 10604) | function readUInt16(buffer, offset, isBigEndian, noAssert) {
  function readUInt32 (line 10644) | function readUInt32(buffer, offset, isBigEndian, noAssert) {
  function readInt16 (line 10757) | function readInt16(buffer, offset, isBigEndian, noAssert) {
  function readInt32 (line 10788) | function readInt32(buffer, offset, isBigEndian, noAssert) {
  function readFloat (line 10819) | function readFloat(buffer, offset, isBigEndian, noAssert) {
  function readDouble (line 10840) | function readDouble(buffer, offset, isBigEndian, noAssert) {
  function verifuint (line 10871) | function verifuint(value, max) {
  function writeUInt16 (line 10904) | function writeUInt16(buffer, value, offset, isBigEndian, noAssert) {
  function writeUInt32 (line 10937) | function writeUInt32(buffer, value, offset, isBigEndian, noAssert) {
  function verifsint (line 11009) | function verifsint(value, max, min) {
  function verifIEEE754 (line 11020) | function verifIEEE754(value, max, min) {
  function writeInt16 (line 11052) | function writeInt16(buffer, value, offset, isBigEndian, noAssert) {
  function writeInt32 (line 11084) | function writeInt32(buffer, value, offset, isBigEndian, noAssert) {
  function writeFloat (line 11116) | function writeFloat(buffer, value, offset, isBigEndian, noAssert) {
  function writeDouble (line 11145) | function writeDouble(buffer, value, offset, isBigEndian, noAssert) {
  function b64ToByteArray (line 11210) | function b64ToByteArray(b64) {
  function uint8ToBase64 (line 11250) | function uint8ToBase64(uint8) {

FILE: sublimelinter/modules/libs/jslint/jslint.js
  function array_to_object (line 276) | function array_to_object(array, value) {
  function sanitize (line 654) | function sanitize(a) {
  function add_to_predefined (line 663) | function add_to_predefined(group) {
  function assume (line 670) | function assume() {
  function artifact (line 702) | function artifact(tok) {
  function quit (line 709) | function quit(message, line, character) {
  function warn (line 721) | function warn(code, line, character, a, b, c, d) {
  function stop (line 746) | function stop(code, line, character, a, b, c, d) {
  function expected_at (line 751) | function expected_at(at) {
  function next_line (line 764) | function next_line() {
  function it (line 792) | function it(type, value) {
  function match (line 835) | function match(x) {
  function string (line 864) | function string(x) {
  function number (line 950) | function number(snippet) {
  function comment (line 977) | function comment(snippet, type) {
  function regexp (line 992) | function regexp() {
  function define (line 1322) | function define(kind, token) {
  function peek (line 1373) | function peek(distance) {
  function advance (line 1392) | function advance(id, match) {
  function do_globals (line 1497) | function do_globals() {
  function do_jslint (line 1529) | function do_jslint() {
  function do_properties (line 1566) | function do_properties() {
  function edge (line 1629) | function edge(mode) {
  function step_in (line 1634) | function step_in(mode) {
  function step_out (line 1670) | function step_out(id, symbol) {
  function one_space (line 1685) | function one_space(left, right) {
  function one_space_only (line 1695) | function one_space_only(left, right) {
  function no_space (line 1704) | function no_space(left, right) {
  function no_space_only (line 1713) | function no_space_only(left, right) {
  function spaces (line 1722) | function spaces(left, right) {
  function comma (line 1732) | function comma() {
  function semicolon (line 1745) | function semicolon() {
  function use_strict (line 1759) | function use_strict() {
  function are_similar (line 1774) | function are_similar(a, b) {
  function expression (line 1842) | function expression(rbp, initial) {
  function symbol (line 1908) | function symbol(s, bp) {
  function postscript (line 1919) | function postscript(x) {
  function ultimate (line 1924) | function ultimate(s) {
  function reserve_name (line 1934) | function reserve_name(x) {
  function stmt (line 1942) | function stmt(s, f) {
  function disrupt_stmt (line 1948) | function disrupt_stmt(s, f) {
  function labeled_stmt (line 1953) | function labeled_stmt(s, f) {
  function prefix (line 1958) | function prefix(s, f) {
  function type (line 1999) | function type(s, t, nud) {
  function reserve (line 2009) | function reserve(s, f) {
  function constant (line 2019) | function constant(name) {
  function reservevar (line 2027) | function reservevar(s, v) {
  function infix (line 2037) | function infix(s, p, f, w) {
  function expected_relation (line 2059) | function expected_relation(node, message) {
  function expected_condition (line 2066) | function expected_condition(node, message) {
  function check_relation (line 2102) | function check_relation(node) {
  function relation (line 2130) | function relation(s, eqeq) {
  function lvalue (line 2164) | function lvalue(that, s) {
  function assignop (line 2187) | function assignop(s, op) {
  function bitwise (line 2217) | function bitwise(s, p) {
  function suffix (line 2224) | function suffix(s) {
  function optional_identifier (line 2242) | function optional_identifier(variable) {
  function identifier (line 2253) | function identifier(variable) {
  function statement (line 2264) | function statement() {
  function statements (line 2345) | function statements() {
  function block (line 2381) | function block(kind) {
  function tally_property (line 2423) | function tally_property(name) {
  function paren_check (line 2566) | function paren_check(that) {
  function banger (line 2781) | function banger(that) {
  function property_name (line 3073) | function property_name() {
  function function_parameters (line 3104) | function function_parameters() {
  function do_function (line 3129) | function do_function(func, name) {
  function find_duplicate_case (line 3521) | function find_duplicate_case(value) {
  function optional_label (line 3771) | function optional_label(that) {
  function json_value (line 3858) | function json_value() {
  function unique (line 4043) | function unique(array) {
  function selects (line 4070) | function selects(name) {
  function detail (line 4137) | function detail(h, array) {

FILE: sublimelinter/modules/libs/pep8.py
  function tabs_or_spaces (line 119) | def tabs_or_spaces(physical_line, indent_char):
  function tabs_obsolete (line 139) | def tabs_obsolete(physical_line):
  function trailing_whitespace (line 152) | def trailing_whitespace(physical_line):
  function trailing_blank_lines (line 182) | def trailing_blank_lines(physical_line, lines, line_number):
  function missing_newline (line 193) | def missing_newline(physical_line):
  function maximum_line_length (line 203) | def maximum_line_length(physical_line, max_line_length):
  function blank_lines (line 235) | def blank_lines(logical_line, blank_lines, indent_level, line_number,
  function extraneous_whitespace (line 273) | def extraneous_whitespace(logical_line):
  function whitespace_around_keywords (line 306) | def whitespace_around_keywords(logical_line):
  function missing_whitespace (line 330) | def missing_whitespace(logical_line):
  function indentation (line 357) | def indentation(logical_line, previous_logical, indent_char,
  function continued_indentation (line 384) | def continued_indentation(logical_line, tokens, indent_level, hang_closing,
  function whitespace_before_parameters (line 555) | def whitespace_before_parameters(logical_line, tokens):
  function whitespace_around_operator (line 589) | def whitespace_around_operator(logical_line):
  function missing_whitespace_around_operator (line 616) | def missing_whitespace_around_operator(logical_line, tokens):
  function whitespace_around_comma (line 713) | def whitespace_around_comma(logical_line):
  function whitespace_around_named_parameter_equals (line 735) | def whitespace_around_named_parameter_equals(logical_line, tokens):
  function whitespace_before_inline_comment (line 771) | def whitespace_before_inline_comment(logical_line, tokens):
  function imports_on_separate_lines (line 800) | def imports_on_separate_lines(logical_line):
  function compound_statements (line 820) | def compound_statements(logical_line):
  function explicit_line_join (line 866) | def explicit_line_join(logical_line, tokens):
  function comparison_to_singleton (line 901) | def comparison_to_singleton(logical_line, noqa):
  function comparison_type (line 931) | def comparison_type(logical_line):
  function python_3000_has_key (line 954) | def python_3000_has_key(logical_line):
  function python_3000_raise_comma (line 967) | def python_3000_raise_comma(logical_line):
  function python_3000_not_equal (line 985) | def python_3000_not_equal(logical_line):
  function python_3000_backticks (line 999) | def python_3000_backticks(logical_line):
  function readlines (line 1019) | def readlines(filename):
  function readlines (line 1029) | def readlines(filename):
  function stdin_get_value (line 1044) | def stdin_get_value():
  function expand_indent (line 1050) | def expand_indent(line):
  function mute_string (line 1079) | def mute_string(text):
  function parse_udiff (line 1100) | def parse_udiff(diff, patterns=None, parent='.'):
  function filename_match (line 1125) | def filename_match(filename, patterns, default=True):
  function register_check (line 1143) | def register_check(check, codes=None):
  function init_checks_registry (line 1163) | def init_checks_registry():
  class Checker (line 1174) | class Checker(object):
    method __init__ (line 1179) | def __init__(self, filename=None, lines=None,
    method report_invalid_syntax (line 1218) | def report_invalid_syntax(self):
    method readline (line 1231) | def readline(self):
    method readline_check_physical (line 1240) | def readline_check_physical(self):
    method run_check (line 1250) | def run_check(self, check, argument_names):
    method check_physical (line 1259) | def check_physical(self, line):
    method build_tokens_line (line 1272) | def build_tokens_line(self):
    method check_logical (line 1312) | def check_logical(self):
    method check_ast (line 1339) | def check_ast(self):
    method generate_tokens (line 1350) | def generate_tokens(self):
    method check_all (line 1360) | def check_all(self, expected=None, line_offset=0):
  class BaseReport (line 1411) | class BaseReport(object):
    method __init__ (line 1415) | def __init__(self, options):
    method start (line 1424) | def start(self):
    method stop (line 1428) | def stop(self):
    method init_file (line 1432) | def init_file(self, filename, lines, expected, line_offset):
    method increment_logical_line (line 1442) | def increment_logical_line(self):
    method error (line 1446) | def error(self, line_number, offset, text, check):
    method get_file_results (line 1465) | def get_file_results(self):
    method get_count (line 1469) | def get_count(self, prefix=''):
    method get_statistics (line 1474) | def get_statistics(self, prefix=''):
    method print_statistics (line 1486) | def print_statistics(self, prefix=''):
    method print_benchmark (line 1491) | def print_benchmark(self):
  class FileReport (line 1501) | class FileReport(BaseReport):
  class StandardReport (line 1506) | class StandardReport(BaseReport):
    method __init__ (line 1509) | def __init__(self, options):
    method init_file (line 1517) | def init_file(self, filename, lines, expected, line_offset):
    method error (line 1523) | def error(self, line_number, offset, text, check):
    method get_file_results (line 1532) | def get_file_results(self):
  class DiffReport (line 1553) | class DiffReport(StandardReport):
    method __init__ (line 1556) | def __init__(self, options):
    method error (line 1560) | def error(self, line_number, offset, text, check):
  class StyleGuide (line 1566) | class StyleGuide(object):
    method __init__ (line 1569) | def __init__(self, *args, **kwargs):
    method init_report (line 1607) | def init_report(self, reporter=None):
    method check_files (line 1612) | def check_files(self, paths=None):
    method input_file (line 1630) | def input_file(self, filename, lines=None, expected=None, line_offset=0):
    method input_dir (line 1638) | def input_dir(self, dirname):
    method excluded (line 1660) | def excluded(self, filename, parent=None):
    method ignore_code (line 1673) | def ignore_code(self, code):
    method get_checks (line 1684) | def get_checks(self, argument_name):
  function get_parser (line 1697) | def get_parser(prog='pep8', version=__version__):
  function read_config (line 1757) | def read_config(options, args, arglist, parser):
  function process_options (line 1808) | def process_options(arglist=None, parse_argv=False, config_file=None,
  function _main (line 1855) | def _main():

FILE: sublimelinter/modules/libs/pyflakes/api.py
  function check (line 17) | def check(codeString, filename, reporter=None):
  function checkPath (line 65) | def checkPath(filename, reporter=None):
  function iterSourceCode (line 91) | def iterSourceCode(paths):
  function checkRecursive (line 109) | def checkRecursive(paths, reporter):
  function main (line 125) | def main(prog=None):

FILE: sublimelinter/modules/libs/pyflakes/checker.py
  function getNodeType (line 30) | def getNodeType(node_class):
  function getNodeType (line 34) | def getNodeType(node_class):
  function getAlternatives (line 39) | def getAlternatives(n):
  function getAlternatives (line 45) | def getAlternatives(n):
  class _FieldsOrder (line 52) | class _FieldsOrder(dict):
    method _get_fields (line 55) | def _get_fields(self, node_class):
    method __missing__ (line 66) | def __missing__(self, node_class):
  function iter_child_nodes (line 71) | def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()):
  class Binding (line 87) | class Binding(object):
    method __init__ (line 99) | def __init__(self, name, source):
    method __str__ (line 104) | def __str__(self):
    method __repr__ (line 107) | def __repr__(self):
    method redefines (line 113) | def redefines(self, other):
  class Definition (line 117) | class Definition(Binding):
  class Importation (line 123) | class Importation(Definition):
    method __init__ (line 132) | def __init__(self, name, source):
    method redefines (line 138) | def redefines(self, other):
  class Argument (line 144) | class Argument(Binding):
  class Assignment (line 150) | class Assignment(Binding):
  class FunctionDefinition (line 160) | class FunctionDefinition(Definition):
  class ClassDefinition (line 164) | class ClassDefinition(Definition):
  class ExportBinding (line 168) | class ExportBinding(Binding):
    method __init__ (line 183) | def __init__(self, name, source, scope):
  class Scope (line 195) | class Scope(dict):
    method __repr__ (line 198) | def __repr__(self):
  class ClassScope (line 203) | class ClassScope(Scope):
  class FunctionScope (line 207) | class FunctionScope(Scope):
    method __init__ (line 217) | def __init__(self):
    method unusedAssignments (line 224) | def unusedAssignments(self):
  class GeneratorScope (line 235) | class GeneratorScope(Scope):
  class ModuleScope (line 239) | class ModuleScope(Scope):
  function getNodeName (line 248) | def getNodeName(node):
  class Checker (line 256) | class Checker(object):
    method __init__ (line 279) | def __init__(self, tree, filename='(none)', builtins=None,
    method deferFunction (line 307) | def deferFunction(self, callable):
    method deferAssignment (line 318) | def deferAssignment(self, callable):
    method runDeferred (line 325) | def runDeferred(self, deferred):
    method scope (line 335) | def scope(self):
    method popScope (line 338) | def popScope(self):
    method checkDeadScopes (line 341) | def checkDeadScopes(self):
    method pushScope (line 375) | def pushScope(self, scopeClass=FunctionScope):
    method report (line 378) | def report(self, messageClass, *args, **kwargs):
    method getParent (line 381) | def getParent(self, node):
    method getCommonAncestor (line 388) | def getCommonAncestor(self, lnode, rnode, stop):
    method descendantOf (line 401) | def descendantOf(self, node, ancestors, stop):
    method differentForks (line 407) | def differentForks(self, lnode, rnode):
    method addBinding (line 418) | def addBinding(self, node, value):
    method getNodeHandler (line 453) | def getNodeHandler(self, node_class):
    method handleNodeLoad (line 461) | def handleNodeLoad(self, node):
    method handleNodeStore (line 500) | def handleNodeStore(self, node):
    method handleNodeDelete (line 533) | def handleNodeDelete(self, node):
    method handleChildren (line 545) | def handleChildren(self, tree, omit=None):
    method isLiteralTupleUnpacking (line 549) | def isLiteralTupleUnpacking(self, node):
    method isDocstring (line 556) | def isDocstring(self, node):
    method getDocstring (line 564) | def getDocstring(self, node):
    method handleNode (line 573) | def handleNode(self, node, parent):
    method handleDoctests (line 597) | def handleDoctests(self, node):
    method ignore (line 629) | def ignore(self, node):
    method GLOBAL (line 659) | def GLOBAL(self, node):
    method GENERATOREXP (line 668) | def GENERATOREXP(self, node):
    method NAME (line 675) | def NAME(self, node):
    method RETURN (line 695) | def RETURN(self, node):
    method YIELD (line 700) | def YIELD(self, node):
    method FUNCTIONDEF (line 706) | def FUNCTIONDEF(self, node):
    method LAMBDA (line 714) | def LAMBDA(self, node):
    method CLASSDEF (line 795) | def CLASSDEF(self, node):
    method AUGASSIGN (line 816) | def AUGASSIGN(self, node):
    method IMPORT (line 821) | def IMPORT(self, node):
    method IMPORTFROM (line 827) | def IMPORTFROM(self, node):
    method TRY (line 846) | def TRY(self, node):
    method EXCEPTHANDLER (line 865) | def EXCEPTHANDLER(self, node):

FILE: sublimelinter/modules/libs/pyflakes/messages.py
  class Message (line 6) | class Message(object):
    method __init__ (line 10) | def __init__(self, filename, loc):
    method __str__ (line 15) | def __str__(self):
  class UnusedImport (line 20) | class UnusedImport(Message):
    method __init__ (line 23) | def __init__(self, filename, loc, name):
  class RedefinedWhileUnused (line 28) | class RedefinedWhileUnused(Message):
    method __init__ (line 31) | def __init__(self, filename, loc, name, orig_loc):
  class RedefinedInListComp (line 36) | class RedefinedInListComp(Message):
    method __init__ (line 39) | def __init__(self, filename, loc, name, orig_loc):
  class ImportShadowedByLoopVar (line 44) | class ImportShadowedByLoopVar(Message):
    method __init__ (line 47) | def __init__(self, filename, loc, name, orig_loc):
  class ImportStarUsed (line 52) | class ImportStarUsed(Message):
    method __init__ (line 55) | def __init__(self, filename, loc, modname):
  class UndefinedName (line 60) | class UndefinedName(Message):
    method __init__ (line 63) | def __init__(self, filename, loc, name):
  class DoctestSyntaxError (line 68) | class DoctestSyntaxError(Message):
    method __init__ (line 71) | def __init__(self, filename, loc, position=None):
  class UndefinedExport (line 78) | class UndefinedExport(Message):
    method __init__ (line 81) | def __init__(self, filename, loc, name):
  class UndefinedLocal (line 86) | class UndefinedLocal(Message):
    method __init__ (line 90) | def __init__(self, filename, loc, name, orig_loc):
  class DuplicateArgument (line 95) | class DuplicateArgument(Message):
    method __init__ (line 98) | def __init__(self, filename, loc, name):
  class Redefined (line 103) | class Redefined(Message):
    method __init__ (line 106) | def __init__(self, filename, loc, name, orig_loc):
  class LateFutureImport (line 111) | class LateFutureImport(Message):
    method __init__ (line 114) | def __init__(self, filename, loc, names):
  class UnusedVariable (line 119) | class UnusedVariable(Message):
    method __init__ (line 126) | def __init__(self, filename, loc, names):
  class ReturnWithArgsInsideGenerator (line 131) | class ReturnWithArgsInsideGenerator(Message):

FILE: sublimelinter/modules/libs/pyflakes/reporter.py
  class Reporter (line 9) | class Reporter(object):
    method __init__ (line 14) | def __init__(self, warningStream, errorStream):
    method unexpectedError (line 28) | def unexpectedError(self, filename, msg):
    method syntaxError (line 39) | def syntaxError(self, filename, msg, lineno, offset, text):
    method flake (line 67) | def flake(self, message):
  function _makeDefaultReporter (line 77) | def _makeDefaultReporter():

FILE: sublimelinter/modules/lua.py
  class Linter (line 11) | class Linter(BaseLinter):
    method parse_errors (line 13) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/notes.py
  class Linter (line 16) | class Linter(BaseLinter):
    method built_in_check (line 19) | def built_in_check(self, view, code, filename):
    method select_annotations (line 28) | def select_annotations(self, view):
    method extract_annotations (line 32) | def extract_annotations(self, code, view, filename):
    method find_all (line 66) | def find_all(self, text, string, view):

FILE: sublimelinter/modules/objective-j.py
  class Linter (line 46) | class Linter(BaseLinter):
    method built_in_check (line 47) | def built_in_check(self, view, code, filename):
    method parse_errors (line 52) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/perl.py
  class Linter (line 11) | class Linter(BaseLinter):
    method __init__ (line 15) | def __init__(self, config):
    method get_executable (line 19) | def get_executable(self, view):
    method get_lint_args (line 34) | def get_lint_args(self, view, code, filename):
    method parse_errors (line 40) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/php.py
  class Linter (line 12) | class Linter(BaseLinter):
    method parse_errors (line 13) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/puppet-lint.py
  class Linter (line 19) | class Linter(BaseLinter):
    method parse_errors (line 20) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/puppet.py
  class Linter (line 14) | class Linter(BaseLinter):
    method parse_errors (line 15) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/python.py
  class LOC (line 57) | class LOC(object):
    method __init__ (line 58) | def __init__(self, lineno):
  class PythonLintError (line 62) | class PythonLintError(pyflakes.messages.Message):
    method __init__ (line 64) | def __init__(self, filename, loc, level, message, message_args, offset...
  class Pep8Error (line 77) | class Pep8Error(PythonLintError):
    method __init__ (line 79) | def __init__(self, filename, loc, offset, code, text):
  class Pep8Warning (line 85) | class Pep8Warning(PythonLintError):
    method __init__ (line 87) | def __init__(self, filename, loc, offset, code, text):
  class OffsetError (line 93) | class OffsetError(PythonLintError):
    method __init__ (line 95) | def __init__(self, filename, loc, text, offset):
  class PythonError (line 99) | class PythonError(PythonLintError):
    method __init__ (line 101) | def __init__(self, filename, loc, text):
  class Linter (line 105) | class Linter(BaseLinter):
    method pyflakes_check (line 106) | def pyflakes_check(self, code, filename, ignore=None):
    method pep8_check (line 150) | def pep8_check(self, code, filename, ignore=None):
    method built_in_check (line 201) | def built_in_check(self, view, code, filename):
    method parse_errors (line 215) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/ruby-lint.py
  class Linter (line 13) | class Linter(BaseLinter):
    method parse_errors (line 15) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/ruby.py
  class Linter (line 12) | class Linter(BaseLinter):
    method parse_errors (line 13) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/squirrel.py
  class Linter (line 16) | class Linter(BaseLinter):
    method parse_errors (line 18) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/sublime_pylint.py
  class Linter (line 18) | class Linter(BaseLinter):
    method get_executable (line 19) | def get_executable(self, view):
    method built_in_check (line 22) | def built_in_check(self, view, code, filename):
    method remove_unwanted (line 47) | def remove_unwanted(self, errors):
    method parse_errors (line 65) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...

FILE: sublimelinter/modules/xml.py
  class Linter (line 12) | class Linter(BaseLinter):
    method parse_errors (line 13) | def parse_errors(self, view, errors, lines, errorUnderlines, violation...
Condensed preview — 87 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,199K chars).
[
  {
    "path": ".codeintel/config",
    "chars": 167,
    "preview": "{\n\t\"Python\": {\n\t\t\"pythonExtraPaths\": [\n\t\t\t\"libs\",\n\t\t\t\"~/Applications/Sublime Text 2.app/Contents/MacOS\",\n\t\t\t\"/Applicatio"
  },
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": ".git\n.hg\n.svn\n\n*.pyc\n*.pyo\n\n.DS_Store\n"
  },
  {
    "path": "Default (Linux).sublime-keymap",
    "chars": 235,
    "preview": "[\n    { \"keys\": [\"ctrl+alt+l\"], \"command\": \"sublimelinter\", \"args\": {\"action\": \"lint\"} },\n    { \"keys\": [\"ctrl+alt+e\"], "
  },
  {
    "path": "Default (OSX).sublime-keymap",
    "chars": 241,
    "preview": "[\n    { \"keys\": [\"ctrl+super+l\"], \"command\": \"sublimelinter\", \"args\": {\"action\": \"lint\"} },\n    { \"keys\": [\"ctrl+super+e"
  },
  {
    "path": "Default (Windows).sublime-keymap",
    "chars": 235,
    "preview": "[\n    { \"keys\": [\"ctrl+alt+l\"], \"command\": \"sublimelinter\", \"args\": {\"action\": \"lint\"} },\n    { \"keys\": [\"ctrl+alt+e\"], "
  },
  {
    "path": "Default.sublime-commands",
    "chars": 1623,
    "preview": "[\n    {\n        \"caption\": \"SublimeLinter: Lint Current File\",\n        \"command\": \"sublimelinter_lint\",\n        \"args\": "
  },
  {
    "path": "LICENSE",
    "chars": 1111,
    "preview": "Copyright Germán M. Bravo, Aparajita Fishman, Jacob Swartwood, and other\ncontributors.\n\nPermission is hereby granted, fr"
  },
  {
    "path": "Main.sublime-menu",
    "chars": 4480,
    "preview": "[\n    {\n        \"caption\": \"Preferences\",\n        \"mnemonic\": \"n\",\n        \"id\": \"preferences\",\n        \"children\":\n    "
  },
  {
    "path": "README.md",
    "chars": 25574,
    "preview": "SublimeLinter\n=============\n\n## SublimeLinter 3 has landed!\n\nSublimeLinter for Sublime Text 3 is [here](https://github.c"
  },
  {
    "path": "SublimeLinter.py",
    "chars": 31563,
    "preview": "from functools import partial\nimport os\nimport re\nimport sys\nimport time\nimport threading\n\nimport sublime\nimport sublime"
  },
  {
    "path": "SublimeLinter.sublime-settings",
    "chars": 8539,
    "preview": "/*\n    SublimeLinter default settings\n*/\n{\n    /*\n        Sets the mode in which SublimeLinter runs:\n\n        true - Lin"
  },
  {
    "path": "changelog.txt",
    "chars": 15856,
    "preview": "SublimeLinter 1.5.1 changelog\n=============================\n\nNEW FEATURES\n------------\n- SublimeLinter keeps its setting"
  },
  {
    "path": "messages/1.5.1.txt",
    "chars": 3316,
    "preview": "SublimeLinter 1.5.1 changelog\n=============================\n\nNEW FEATURES\n------------\n- SublimeLinter keeps its setting"
  },
  {
    "path": "messages/1.5.2.txt",
    "chars": 435,
    "preview": "SublimeLinter 1.5.2 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Fixed a problem with messages"
  },
  {
    "path": "messages/1.5.3.txt",
    "chars": 282,
    "preview": "SublimeLinter 1.5.3 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Annotations have been fixed.\n"
  },
  {
    "path": "messages/1.5.4.txt",
    "chars": 272,
    "preview": "SublimeLinter 1.5.4 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- jshint.js has been updated to"
  },
  {
    "path": "messages/1.5.5.txt",
    "chars": 164,
    "preview": "SublimeLinter 1.5.5 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- This change log is available "
  },
  {
    "path": "messages/1.5.6.txt",
    "chars": 435,
    "preview": "SublimeLinter 1.5.6 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Fixed a problem with messages"
  },
  {
    "path": "messages/1.5.7.txt",
    "chars": 454,
    "preview": "SublimeLinter 1.5.7 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- node.js is the preferred Java"
  },
  {
    "path": "messages/1.6.0.txt",
    "chars": 1934,
    "preview": "SublimeLinter 1.6.0 changelog\n=============================\n\nNEW FEATURES\n------------\n- Simpler abstraction of JavaScri"
  },
  {
    "path": "messages/1.6.1.txt",
    "chars": 321,
    "preview": "SublimeLinter 1.6.1 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Fixed an issue (#141) with JS"
  },
  {
    "path": "messages/1.6.10.txt",
    "chars": 340,
    "preview": "SublimeLinter 1.6.10 changelog\n==============================\n\nCHANGES/FIXES\n-------------\n- Puppet validation supports "
  },
  {
    "path": "messages/1.6.11.txt",
    "chars": 135,
    "preview": "SublimeLinter 1.6.11 changelog\n==============================\n\nCHANGES/FIXES\n-------------\n- Github (nodeload) zip url s"
  },
  {
    "path": "messages/1.6.12.txt",
    "chars": 212,
    "preview": "SublimeLinter 1.6.12 changelog\n==============================\n\nCHANGES/FIXES\n-------------\n- Cpplint no longer uses a te"
  },
  {
    "path": "messages/1.6.13.txt",
    "chars": 312,
    "preview": "SublimeLinter 1.6.13 changelog\n==============================\n\nCHANGES/FIXES\n-------------\n- Updated PEP8 to version v1."
  },
  {
    "path": "messages/1.6.2.txt",
    "chars": 572,
    "preview": "SublimeLinter 1.6.2 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Replaced the default perl lin"
  },
  {
    "path": "messages/1.6.3.txt",
    "chars": 725,
    "preview": "SublimeLinter 1.6.3 changelog\n=============================\n\nNEW FEATURES\n------------\n- Support for `.jshintrc` files. "
  },
  {
    "path": "messages/1.6.4.txt",
    "chars": 889,
    "preview": "SublimeLinter 1.6.4 changelog\n=============================\n\nIMPORTANT!!\n-----------\nPlease note that the SublimeLinter "
  },
  {
    "path": "messages/1.6.5.txt",
    "chars": 690,
    "preview": "SublimeLinter 1.6.5 changelog\n=============================\n\nNEW FEATURES\n------------\n- Added a (Ruby) Haml linter base"
  },
  {
    "path": "messages/1.6.6.txt",
    "chars": 568,
    "preview": "SublimeLinter 1.6.6 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- JSHint now shows underlines a"
  },
  {
    "path": "messages/1.6.7.txt",
    "chars": 617,
    "preview": "SublimeLinter 1.6.7 changelog\n=============================\n\nNEW FEATURES\n------------\n- Puppet linting is now supported"
  },
  {
    "path": "messages/1.6.8.txt",
    "chars": 1013,
    "preview": "SublimeLinter 1.6.8 changelog\n=============================\n\nNEW FEATURES\n------------\n- HTML5 linting support via `tidy"
  },
  {
    "path": "messages/1.6.9.txt",
    "chars": 438,
    "preview": "SublimeLinter 1.6.9 changelog\n=============================\n\nNEW FEATURES\n------------\n- C/C++ lint via `cppcheck`. Also"
  },
  {
    "path": "messages/1.7.0.txt",
    "chars": 1146,
    "preview": "SublimeLinter 1.7.0 changelog\n=============================\n\nNEW FEATURES\n------------\n- Add \"Quick Start\" section to th"
  },
  {
    "path": "messages/1.7.1.txt",
    "chars": 327,
    "preview": "SublimeLinter 1.7.1 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Update JSLint to latest\n- Upd"
  },
  {
    "path": "messages/1.7.2.txt",
    "chars": 265,
    "preview": "SublimeLinter 1.7.2 changelog\n=============================\n\nCHANGES/FIXES\n-------------\n- Update JSHint to v2.1.8\n- Fix"
  },
  {
    "path": "messages/SublimeLinter3-update1.txt",
    "chars": 836,
    "preview": "=====================\nSublimeLinter for ST3\nFundraising update\n=====================\n\nAs of October 28, we have raised $"
  },
  {
    "path": "messages/SublimeLinter3-update2.txt",
    "chars": 1092,
    "preview": "=====================\nSublimeLinter for ST3\nFundraising update\nOctober 31, 2013\n=====================\n\nThrough October 3"
  },
  {
    "path": "messages/SublimeLinter3-update3.txt",
    "chars": 1853,
    "preview": "\n  ____        _     _ _                _     _       _              _____\n / ___| _   _| |__ | (_)_ __ ___   ___| |   ("
  },
  {
    "path": "messages/SublimeLinter3.txt",
    "chars": 1290,
    "preview": "=====================\nSublimeLinter for ST3\nWe need your help!\n=====================\n\nAccording to the Package Control s"
  },
  {
    "path": "messages/install.txt",
    "chars": 2080,
    "preview": "SublimeLinter\n=============\n\nSublimeLinter is a plugin that supports \"lint\" programs (known as \"linters\"). SublimeLinter"
  },
  {
    "path": "messages.json",
    "chars": 1098,
    "preview": "{\n    \"install\": \"messages/install.txt\",\n    \"1.5.1\": \"messages/1.5.1.txt\",\n    \"1.5.2\": \"messages/1.5.2.txt\",\n    \"1.5."
  },
  {
    "path": "package_control.json",
    "chars": 897,
    "preview": "{\n\t\"schema_version\": \"1.1\",\n\t\"packages\": [\n\t\t{\n\t\t\t\"name\": \"SublimeLinter\",\n\t\t\t\"description\": \"Inline lint highlighting f"
  },
  {
    "path": "sublimelinter/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "sublimelinter/loader.py",
    "chars": 4788,
    "preview": "# Note: Unlike linter modules, changes made to this module will NOT take effect until\n# Sublime Text is restarted.\n\nimpo"
  },
  {
    "path": "sublimelinter/modules/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "sublimelinter/modules/base_linter.py",
    "chars": 16096,
    "preview": "# base_linter.py - base class for linters\n\nimport os\nimport os.path\nimport json\nimport re\nimport subprocess\n\nimport subl"
  },
  {
    "path": "sublimelinter/modules/c.py",
    "chars": 1256,
    "preview": "import re\n\nfrom base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE\n\nCONFIG = {\n    'language': 'C',\n    'executable':"
  },
  {
    "path": "sublimelinter/modules/c_cpplint.py",
    "chars": 650,
    "preview": "import re\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'c_cpplint',\n    'executable': 'cpplint.py',\n "
  },
  {
    "path": "sublimelinter/modules/coffeescript.py",
    "chars": 1069,
    "preview": "import re\nimport os\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'CoffeeScript',\n    'executable': 'c"
  },
  {
    "path": "sublimelinter/modules/css.py",
    "chars": 1172,
    "preview": "import json\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'CSS'\n}\n\n\nclass Linter(BaseLinter):\n    def "
  },
  {
    "path": "sublimelinter/modules/git_commit_message.py",
    "chars": 4009,
    "preview": "from base_linter import BaseLinter\n\n\nCONFIG = {\n    'language': 'Git Commit Message'\n}\n\n\nclass ErrorType:\n    WARNING = "
  },
  {
    "path": "sublimelinter/modules/haml.py",
    "chars": 604,
    "preview": "import re\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'Ruby Haml',\n    'executable': 'haml',\n    'li"
  },
  {
    "path": "sublimelinter/modules/haskell.py",
    "chars": 1505,
    "preview": "import re\nfrom base_linter import BaseLinter, INPUT_METHOD_FILE\n\n\nCONFIG = {\n    'language': 'haskell',\n    'executable'"
  },
  {
    "path": "sublimelinter/modules/html.py",
    "chars": 1345,
    "preview": "# Example error messages\n#\n# line 1 column 1 - Warning: missing <!DOCTYPE> declaration\n# line 200 column 1 - Warning: di"
  },
  {
    "path": "sublimelinter/modules/java.py",
    "chars": 1743,
    "preview": "import os\nimport os.path\nimport re\n\nfrom base_linter import BaseLinter, INPUT_METHOD_FILE\n\nCONFIG = {\n    'language': 'J"
  },
  {
    "path": "sublimelinter/modules/javascript.py",
    "chars": 2984,
    "preview": "import json\nimport re\nimport subprocess\n\nfrom base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE\n\nCONFIG = {\n    'lan"
  },
  {
    "path": "sublimelinter/modules/libs/capp_lint.py",
    "chars": 43463,
    "preview": "#!/usr/bin/env python\n#\n# capp_lint.py - Check Objective-J source code formatting,\n# according to Cappuccino standards:\n"
  },
  {
    "path": "sublimelinter/modules/libs/csslint/csslint-node.js",
    "chars": 304941,
    "preview": "/*!\nCSSLint\nCopyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.\n\nPermission is hereby granted"
  },
  {
    "path": "sublimelinter/modules/libs/csslint/linter.js",
    "chars": 1289,
    "preview": "/*jslint node: true, sloppy: true */\n/*globals LINTER_PATH, load */\n\nvar CSSLint = require(\"./csslint-node\").CSSLint;\n\ne"
  },
  {
    "path": "sublimelinter/modules/libs/jsengines/jsc.js",
    "chars": 1084,
    "preview": "/*jshint boss: true, evil: true */\n/*globals load quit readline lint JSHINT */\n\n// usage:\n//   jsc ${envHome}/jsc.js -- "
  },
  {
    "path": "sublimelinter/modules/libs/jsengines/node.js",
    "chars": 949,
    "preview": "/*jshint node:true */\n\n/*\n    usage: node /path/to/node.js /path/to/linter/ [\"{option1:true,option2:false}\"]\n    */\n\nvar"
  },
  {
    "path": "sublimelinter/modules/libs/jshint/jshint.js",
    "chars": 357177,
    "preview": "var window = {};//2.1.8\nvar JSHINT;\n(function () {\nvar require;\nrequire=(function(e,t,n){function i(n,s){if(!t[n]){if(!e"
  },
  {
    "path": "sublimelinter/modules/libs/jshint/linter.js",
    "chars": 591,
    "preview": "/*jshint node: true */\n/*globals LINTER_PATH load */\n\nvar JSHINT = require(\"./jshint\").JSHINT;\n\nexports.lint = function "
  },
  {
    "path": "sublimelinter/modules/libs/jslint/jslint.js",
    "chars": 143833,
    "preview": "// jslint.js\n// 2013-07-24\n\n// Copyright (c) 2002 Douglas Crockford  (www.JSLint.com)\n\n// Permission is hereby granted, "
  },
  {
    "path": "sublimelinter/modules/libs/jslint/linter.js",
    "chars": 482,
    "preview": "/*jslint node: true, sloppy: true */\n/*globals LINTER_PATH, load */\n\nvar JSLINT = require(\"./jslint\").JSLINT;\n\nexports.l"
  },
  {
    "path": "sublimelinter/modules/libs/pep8.py",
    "chars": 70827,
    "preview": "#!/usr/bin/env python\n# pep8.py - Check Python source code formatting, according to PEP 8\n# Copyright (C) 2006-2009 Joha"
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/__init__.py",
    "chars": 23,
    "preview": "\n__version__ = '0.8.1'\n"
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/__main__.py",
    "chars": 126,
    "preview": "from pyflakes.api import main\n\n# python -m pyflakes (with Python >= 2.7)\nif __name__ == '__main__':\n    main(prog='pyfla"
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/api.py",
    "chars": 4209,
    "preview": "\"\"\"\nAPI for the command-line I{pyflakes} tool.\n\"\"\"\nfrom __future__ import with_statement\n\nimport sys\nimport os\nimport _a"
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/checker.py",
    "chars": 31446,
    "preview": "\"\"\"\nMain module.\n\nImplement the central Checker class.\nAlso, it models the Bindings and Scopes.\n\"\"\"\nimport doctest\nimpor"
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/messages.py",
    "chars": 3808,
    "preview": "\"\"\"\nProvide the class Message and its subclasses.\n\"\"\"\n\n\nclass Message(object):\n    message = ''\n    message_args = ()\n\n "
  },
  {
    "path": "sublimelinter/modules/libs/pyflakes/reporter.py",
    "chars": 2666,
    "preview": "\"\"\"\nProvide the Reporter class.\n\"\"\"\n\nimport re\nimport sys\n\n\nclass Reporter(object):\n    \"\"\"\n    Formats the results of p"
  },
  {
    "path": "sublimelinter/modules/lua.py",
    "chars": 607,
    "preview": "import re\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'Lua',\n    'executable': 'luac',\n    'lint_args"
  },
  {
    "path": "sublimelinter/modules/notes.py",
    "chars": 2249,
    "preview": "'''notes.py\n\nUsed to highlight user-defined \"annotations\" such as TODO, README, etc.,\ndepending user choice.\n'''\n\nimport"
  },
  {
    "path": "sublimelinter/modules/objective-j.py",
    "chars": 2519,
    "preview": "# objective-j.py - Lint checking for Objective-J - given filename and contents of the code:\n# It provides a list of line"
  },
  {
    "path": "sublimelinter/modules/perl.py",
    "chars": 2238,
    "preview": "import re\nimport subprocess\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'Perl'\n}\n\n\nclass Linter(Base"
  },
  {
    "path": "sublimelinter/modules/php.py",
    "chars": 693,
    "preview": "import re\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'PHP',\n    'executable': 'php',\n    'lint_args"
  },
  {
    "path": "sublimelinter/modules/puppet-lint.py",
    "chars": 1608,
    "preview": "# -*- coding: utf-8 -*-\n# puppet-lint.py - sublimelint package for checking puppet files\n# Requires:\n# gem install puppe"
  },
  {
    "path": "sublimelinter/modules/puppet.py",
    "chars": 1489,
    "preview": "import re\n\nfrom base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE\n\nCONFIG = {\n    'language': 'Puppet',\n    'executa"
  },
  {
    "path": "sublimelinter/modules/python.py",
    "chars": 11244,
    "preview": "# -*- coding: utf-8 -*-\n# python.py - Lint checking for Python - given filename and contents of the code:\n# It provides "
  },
  {
    "path": "sublimelinter/modules/ruby-lint.py",
    "chars": 1257,
    "preview": "import re\n\nfrom base_linter import BaseLinter, INPUT_METHOD_TEMP_FILE\n\nCONFIG = {\n    'language': 'ruby-lint',\n    'exec"
  },
  {
    "path": "sublimelinter/modules/ruby.py",
    "chars": 601,
    "preview": "import re\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'Ruby',\n    'executable': 'ruby',\n    'lint_ar"
  },
  {
    "path": "sublimelinter/modules/squirrel.py",
    "chars": 852,
    "preview": "# -*- coding: utf-8 -*-\n# squirrel.py - sublimelint package for checking squirrel files\n\nimport re\n\nfrom base_linter imp"
  },
  {
    "path": "sublimelinter/modules/sublime_pylint.py",
    "chars": 2339,
    "preview": "from StringIO import StringIO\nimport tempfile\n\ntry:\n    from pylint import checkers\n    from pylint import lint\n    PYLI"
  },
  {
    "path": "sublimelinter/modules/xml.py",
    "chars": 612,
    "preview": "import re\n\nfrom base_linter import BaseLinter\n\nCONFIG = {\n    'language': 'XML',\n    'executable': 'xmllint',\n    'lint_"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the SublimeLinter/SublimeLinter-for-ST2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 87 files (1.1 MB), approximately 257.8k tokens, and a symbol index with 674 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!