backing up sublime settings
5
Packages/SublimeLinter/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.sublime-project
|
||||
/Context.sublime-menu
|
||||
/Main.sublime-menu
|
||||
0
Packages/SublimeLinter/.no-sublime-package
Normal file
11
Packages/SublimeLinter/.sublimelinterrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"@python": 3,
|
||||
"linters": {
|
||||
"flake8": {
|
||||
"max-line-length": 120
|
||||
},
|
||||
"pep8": {
|
||||
"max-line-length": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/SublimeLinter/.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
language: python
|
||||
python:
|
||||
- "3.3"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- pip install flake8
|
||||
- pip install pep257
|
||||
# command to run tests
|
||||
script:
|
||||
- flake8 . --max-line-length=120 --exclude=linter-plugin-template
|
||||
- pep257 .
|
||||
11
Packages/SublimeLinter/CONTRIBUTING.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## Stop!
|
||||
|
||||
Do **NOT** submit an issue here until you have:
|
||||
|
||||
- Read the [installation](http://sublimelinter.readthedocs.org/en/latest/installation.html) and [troubleshooting](http://sublimelinter.readthedocs.org/en/latest/troubleshooting.html) documentation.
|
||||
|
||||
- Posted a message on the [SublimeLinter support forum](https://groups.google.com/forum/#!forum/sublimelinter).
|
||||
|
||||
- Verified on the support forum that you have actually found a bug in SublimeLinter.
|
||||
|
||||
Thank you!
|
||||
66
Packages/SublimeLinter/Context.sublime-menu.template
Normal file
@@ -0,0 +1,66 @@
|
||||
[
|
||||
{
|
||||
"id": "sublimelinter",
|
||||
"caption": "SublimeLinter",
|
||||
"children":
|
||||
[
|
||||
$menus,
|
||||
{
|
||||
"caption": "Choose Gutter Theme...",
|
||||
"command": "sublimelinter_choose_gutter_theme"
|
||||
},
|
||||
{
|
||||
"caption": "Toggle Linter...",
|
||||
"command": "sublimelinter_toggle_linter", "args":
|
||||
{
|
||||
"which": "all"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "-"
|
||||
},
|
||||
{
|
||||
"caption": "Lint This View",
|
||||
"command": "sublimelinter_lint"
|
||||
},
|
||||
{
|
||||
"caption": "Next Error",
|
||||
"command": "sublimelinter_goto_error", "args": {"direction": "next"}
|
||||
},
|
||||
{
|
||||
"caption": "Previous Error",
|
||||
"command": "sublimelinter_goto_error", "args": {"direction": "previous"}
|
||||
},
|
||||
{
|
||||
"caption": "Show All Errors",
|
||||
"command": "sublimelinter_show_all_errors"
|
||||
},
|
||||
{
|
||||
"caption": "Show Errors on Save",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "show_errors_on_save",
|
||||
"checked": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "-"
|
||||
},
|
||||
{
|
||||
"caption": "Open User Settings",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/SublimeLinter.sublime-settings"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Debug Mode",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "debug",
|
||||
"checked": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
6
Packages/SublimeLinter/Default (Linux).sublime-keymap
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{ "keys": ["ctrl+k", "l"], "command": "sublimelinter_lint" },
|
||||
{ "keys": ["ctrl+k", "n"], "command": "sublimelinter_goto_error", "args": {"direction": "next"} },
|
||||
{ "keys": ["ctrl+k", "p"], "command": "sublimelinter_goto_error", "args": {"direction": "previous"} },
|
||||
{ "keys": ["ctrl+k", "a"], "command": "sublimelinter_show_all_errors" }
|
||||
]
|
||||
6
Packages/SublimeLinter/Default (OSX).sublime-keymap
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{ "keys": ["ctrl+super+l"], "command": "sublimelinter_lint" },
|
||||
{ "keys": ["ctrl+super+e"], "command": "sublimelinter_goto_error", "args": {"direction": "next"} },
|
||||
{ "keys": ["ctrl+super+shift+e"], "command": "sublimelinter_goto_error", "args": {"direction": "previous"} },
|
||||
{ "keys": ["ctrl+super+a"], "command": "sublimelinter_show_all_errors" }
|
||||
]
|
||||
6
Packages/SublimeLinter/Default (Windows).sublime-keymap
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{ "keys": ["ctrl+k", "l"], "command": "sublimelinter_lint" },
|
||||
{ "keys": ["ctrl+k", "n"], "command": "sublimelinter_goto_error", "args": {"direction": "next"} },
|
||||
{ "keys": ["ctrl+k", "p"], "command": "sublimelinter_goto_error", "args": {"direction": "previous"} },
|
||||
{ "keys": ["ctrl+k", "a"], "command": "sublimelinter_show_all_errors" }
|
||||
]
|
||||
193
Packages/SublimeLinter/Default.sublime-commands
Normal file
@@ -0,0 +1,193 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – Default",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/SublimeLinter/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – Default",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/SublimeLinter/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – Default",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/SublimeLinter/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Preferences: SublimeLinter Key Bindings – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "SublimeLinter: Choose Lint Mode",
|
||||
"command": "sublimelinter_choose_lint_mode"
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Choose Mark Style",
|
||||
"command": "sublimelinter_choose_mark_style"
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Choose Gutter Theme",
|
||||
"command": "sublimelinter_choose_gutter_theme"
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Lint This View",
|
||||
"command": "sublimelinter_lint"
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Next Error",
|
||||
"command": "sublimelinter_goto_error", "args": {"direction": "next"}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Previous Error",
|
||||
"command": "sublimelinter_goto_error", "args": {"direction": "previous"}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Show All Errors",
|
||||
"command": "sublimelinter_show_all_errors"
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Show Errors on Save",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "show_errors_on_save",
|
||||
"value": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Don’t Show Errors on Save",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "show_errors_on_save",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: No Column Highlights Entire Line",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "no_column_highlights_line",
|
||||
"value": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: No Column Only Marks Gutter",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "no_column_highlights_line",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Enable Linting",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "@disable",
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Disable Linting",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "@disable",
|
||||
"value": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Enable Debug Mode",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "debug",
|
||||
"value": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Disable Debug Mode",
|
||||
"command": "sublimelinter_toggle_setting", "args":
|
||||
{
|
||||
"setting": "debug",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "SublimeLinter: Toggle Linter",
|
||||
"command": "sublimelinter_toggle_linter", "args":
|
||||
{
|
||||
"which": "all"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Enable Linter",
|
||||
"command": "sublimelinter_toggle_linter", "args":
|
||||
{
|
||||
"which": "disabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "SublimeLinter: Disable Linter",
|
||||
"command": "sublimelinter_toggle_linter", "args":
|
||||
{
|
||||
"which": "enabled"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "SublimeLinter: Create Linter Plugin",
|
||||
"command": "sublimelinter_create_linter_plugin"
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "SublimeLinter: Report (Open Files)",
|
||||
"command": "sublimelinter_report",
|
||||
"args": {"on": "files"}
|
||||
}
|
||||
]
|
||||
17
Packages/SublimeLinter/LICENSE
Normal file
@@ -0,0 +1,17 @@
|
||||
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.
|
||||
95
Packages/SublimeLinter/Main.sublime-menu.template
Normal file
@@ -0,0 +1,95 @@
|
||||
[
|
||||
{
|
||||
"id": "tools",
|
||||
"children":
|
||||
$menus
|
||||
},
|
||||
|
||||
{
|
||||
"caption": "Preferences",
|
||||
"id": "preferences",
|
||||
"mnemonic": "n",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Package Settings",
|
||||
"id": "package-settings",
|
||||
"mnemonic": "P",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "SublimeLinter",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Settings – Default",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/SublimeLinter/SublimeLinter.sublime-settings"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Settings – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/SublimeLinter.sublime-settings"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "-"
|
||||
},
|
||||
{
|
||||
"caption": "Key Bindings – Default",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/SublimeLinter/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
}
|
||||
},
|
||||
{
|
||||
"caption": "Key Bindings – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/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 – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/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 – User",
|
||||
"command": "open_file", "args":
|
||||
{
|
||||
"file": "${packages}/User/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
31
Packages/SublimeLinter/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
SublimeLinter
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/SublimeLinter/SublimeLinter3)
|
||||
|
||||
**To those upgrading from SublimeLinter v1.7:**
|
||||
|
||||
**LINTERS ARE *NOT* INCLUDED WITH SUBLIMELINTER 3.**
|
||||
|
||||
**Please read the [installation documentation](http://sublimelinter.readthedocs.org/en/latest/installation.html)!**
|
||||
|
||||
## About SublimeLinter
|
||||
A framework for interactive code linting in the [Sublime Text 3](http://sublimetext.com/3) editor. The Sublime Text 2 version is no longer being supported; you can find it [here](https://github.com/SublimeLinter/SublimeLinter).
|
||||
|
||||
**Documentation:** See it on [readthedocs.org](http://sublimelinter.readthedocs.org).
|
||||
|
||||
**Support:** Please use the [SublimeLinter google group](https://groups.google.com/forum/#!forum/sublimelinter).
|
||||
|
||||
**Bugs:** Please use the [SublimeLinter google group](https://groups.google.com/forum/#!forum/sublimelinter) to report installation and configuration problems. If you think you have found a bug, please ask about it on the [SublimeLinter google group](https://groups.google.com/forum/#!forum/sublimelinter) first, and after it is confirmed as a bug you can report it on the [issue tracker](https://github.com/SublimeLinter/SublimeLinter3/issues). Please be sure to put your OS and SublimeLinter version in issue.
|
||||
|
||||
**Contributing:** If you would like to submit a fix or enhancement for SublimeLinter, please read the [contributing guidelines](http://sublimelinter.readthedocs.org/contributing.html) first.
|
||||
|
||||
## Share the love!
|
||||
I spent hundreds of hours writing and documenting SublimeLinter 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!
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=55KC77W2MU9VW)
|
||||
|
||||
<a class="coinbase-button" data-code="3265d1a223f01885e92514751e45cc55" data-button-style="custom_large" href="https://coinbase.com/checkouts/3265d1a223f01885e92514751e45cc55">Donate Bitcoins</a><script src="https://coinbase.com/assets/button.js" type="text/javascript"></script>
|
||||
|
||||
## Contributing linter plugins
|
||||
Please see the documentation on [creating linter plugins](http://sublimelinter.readthedocs.org/en/latest/creating_a_linter.html) for more information.
|
||||
9
Packages/SublimeLinter/Side Bar.sublime-menu
Normal file
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "caption": "-"},
|
||||
{
|
||||
"caption": "New Package Control Message",
|
||||
"command": "sublimelinter_new_package_control_message",
|
||||
"args": {"paths": []}
|
||||
},
|
||||
{ "caption": "-"}
|
||||
]
|
||||
35
Packages/SublimeLinter/SublimeLinter.sublime-settings
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"default": {
|
||||
"debug": false,
|
||||
"delay": 0.25,
|
||||
"error_color": "D02000",
|
||||
"gutter_theme": "Packages/SublimeLinter/gutter-themes/Default/Default.gutter-theme",
|
||||
"gutter_theme_excludes": [],
|
||||
"lint_mode": "background",
|
||||
"mark_style": "outline",
|
||||
"no_column_highlights_line": false,
|
||||
"paths": {
|
||||
"linux": [],
|
||||
"osx": [],
|
||||
"windows": []
|
||||
},
|
||||
"python_paths": {
|
||||
"linux": [],
|
||||
"osx": [],
|
||||
"windows": []
|
||||
},
|
||||
"rc_search_limit": 3,
|
||||
"shell_timeout": 10,
|
||||
"show_errors_on_save": false,
|
||||
"show_marks_in_minimap": true,
|
||||
"syntax_map": {
|
||||
"python django": "python",
|
||||
"html 5": "html",
|
||||
"html (django)": "html",
|
||||
"html (rails)": "html",
|
||||
"php": "html"
|
||||
},
|
||||
"warning_color": "DDB700",
|
||||
"wrap_find": true
|
||||
}
|
||||
}
|
||||
1169
Packages/SublimeLinter/commands.py
Normal file
27
Packages/SublimeLinter/create_linter_info.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"javascript": {
|
||||
"platform": "[Node.js](http://nodejs.org) (and [npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager) on Linux)",
|
||||
"installer": "npm install -g {}",
|
||||
"extra_steps": "1. If you are using `nvm` and `zsh`, ensure that the line to load `nvm` is in `.zshenv` and not `.zshrc`.\n\n1. If you are using `zsh` and `oh-my-zsh`, do not load the `nvm` plugin for `oh-my-zsh`.",
|
||||
"comment_re": "r'\\s*/[/*]'"
|
||||
},
|
||||
|
||||
"python": {
|
||||
"platform": "[Python](http://python.org/download/) and [pip](http://www.pip-installer.org/en/latest/installing.html)",
|
||||
"installer": "[sudo] pip install {}",
|
||||
"superclass": "PythonLinter",
|
||||
"attributes": ["module = '{}'", "check_version = False"]
|
||||
},
|
||||
|
||||
"ruby": {
|
||||
"platform": "[Ruby](http://www.ruby-lang.org)",
|
||||
"installer": "[sudo] gem install {}",
|
||||
"extra_steps": "1. If you are using `rbenv` or `rvm`, ensure that they are loaded in your shell’s correct startup file. See [here](http://sublimelinter.readthedocs.org/en/latest/troubleshooting.html#shell-startup-files) for more information.",
|
||||
"superclass": "RubyLinter",
|
||||
"comment_re": "r'\\s*#'"
|
||||
},
|
||||
|
||||
"other": {
|
||||
"installer": "<package manager> install {}"
|
||||
}
|
||||
}
|
||||
1002
Packages/SublimeLinter/fixed-syntaxes/HTML/HTML.tmLanguage
Normal file
@@ -0,0 +1 @@
|
||||
2
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>rhtml</string>
|
||||
<string>erb</string>
|
||||
<string>html.erb</string>
|
||||
</array>
|
||||
<key>foldingStartMarker</key>
|
||||
<string>(?x)
|
||||
(<(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)\b.*?>
|
||||
|<!--(?!.*-->)
|
||||
|\{\s*($|\?>\s*$|//|/\*(.*\*/\s*$|(?!.*?\*/)))
|
||||
)</string>
|
||||
<key>foldingStopMarker</key>
|
||||
<string>(?x)
|
||||
(</(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)>
|
||||
|^\s*-->
|
||||
|(^|\s)\}
|
||||
)</string>
|
||||
<key>keyEquivalent</key>
|
||||
<string>^~R</string>
|
||||
<key>name</key>
|
||||
<string>HTML (Rails)</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string><%+#</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.erb</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>%></string>
|
||||
<key>name</key>
|
||||
<string>comment.block.erb</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
<string><%+(?!>)[-=]?</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.section.embedded.ruby</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>-?%></string>
|
||||
<key>endCaptures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.section.embedded.ruby</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>contentName</key>
|
||||
<string>source.ruby.rails.embedded.html</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.ruby</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(#).*?(?=-?%>)</string>
|
||||
<key>name</key>
|
||||
<string>comment.line.number-sign.ruby</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>include</key>
|
||||
<string>source.ruby.rails</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>include</key>
|
||||
<string>text.html.basic</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>text.html.ruby</string>
|
||||
<key>uuid</key>
|
||||
<string>45D7E1FC-7D0B-4105-A1A2-3D10BB555A5C</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
4
Packages/SublimeLinter/gutter-themes/Blueberry/LICENSE
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
This work (Blueberry icons, by Andrew Zhebrakov) is free of known copyright restrictions.
|
||||
|
||||
http://www.icojam.com/blog/?p=259#more-259
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Blueberry/cross/error.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Blueberry/cross/warning.png
Normal file
|
After Width: | Height: | Size: 807 B |
BIN
Packages/SublimeLinter/gutter-themes/Blueberry/round/error.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Blueberry/round/warning.png
Normal file
|
After Width: | Height: | Size: 807 B |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"colorize": true
|
||||
}
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Circle/error.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Circle/warning.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
|
||||
These icons are free to use in both commercial products as well as personal use.
|
||||
|
||||
http://www.softicons.com/free-icons/toolbar-icons/danish-royalty-free-icons-by-jonasrask-design
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Danish Royalty/error.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Danish Royalty/warning.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"colorize": true
|
||||
}
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Default/error.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Default/warning.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
4
Packages/SublimeLinter/gutter-themes/Hands/LICENSE
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
Freeware, commercial usage allowed.
|
||||
|
||||
http://www.softicons.com/free-icons/web-icons/free-hand-pointer-icons-by-icojoy
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Hands/error.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Hands/warning.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
4
Packages/SublimeLinter/gutter-themes/Knob/LICENSE
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
You are free to use these icons on your software application, website, etc. You're welcome to give credits for the graphics, when used.
|
||||
|
||||
http://www.softicons.com/free-icons/toolbar-icons/knob-buttons-toolbar-icons-by-itweek
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Knob/simple/error.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Knob/simple/warning.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Knob/symbol/error.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Packages/SublimeLinter/gutter-themes/Knob/symbol/warning.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
7
Packages/SublimeLinter/gutter-themes/Koloria/LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
- Koloria Icon Set can be used in open source or commercial projects for free.
|
||||
- When you mention Koloria Icon Set, please refer to this page/download link.
|
||||
- Do not sell the package on graphic stock sites – this pack is intended to be free. You may include the icons in your commercial themes/designs/creations.
|
||||
- Do not claim to be the author of this icon set.
|
||||
|
||||
http://www.graphicrating.com/2012/06/14/koloria-free-icons-set/
|
||||
BIN
Packages/SublimeLinter/gutter-themes/Koloria/error.png
Normal file
|
After Width: | Height: | Size: 962 B |
BIN
Packages/SublimeLinter/gutter-themes/Koloria/warning.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
18
Packages/SublimeLinter/gutter-themes/ProjectIcons/LICENSE
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
Author
|
||||
|
||||
These icons were created by Mihaiciuc Bogdan and are available for download at deviantArt.
|
||||
|
||||
|
||||
License
|
||||
|
||||
These icons are licensed under a Creative-Commons Attribution 3.0 license. You are allowed to share, modify and use these icons in your projects as long as you credit me as the original author.
|
||||
Click here for more information about the license.
|
||||
|
||||
|
||||
Attribution
|
||||
|
||||
If you use these icons, you must credit me, Mihaiciuc Bogdan, as the original author of the icons in your project’s “About” window/page and place a link to http://bogo-d.deviantart.com.
|
||||
|
||||
The suggested format is: “Uses icons from “Project Icons” by Mihaiciuc Bogdan.”
|
||||
The text “Mihaiciuc Bogdan” should link to http://bogo-d.deviantart.com.
|
||||
BIN
Packages/SublimeLinter/gutter-themes/ProjectIcons/error.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Packages/SublimeLinter/gutter-themes/ProjectIcons/warning.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
32
Packages/SublimeLinter/lint/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# lint.__init__
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Ryan Hileman and Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module exports the linter classes and the highlight, linter, persist and util submodules."""
|
||||
|
||||
from .linter import Linter
|
||||
from .python_linter import PythonLinter
|
||||
from .ruby_linter import RubyLinter
|
||||
|
||||
from . import (
|
||||
highlight,
|
||||
linter,
|
||||
persist,
|
||||
util,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'highlight',
|
||||
'Linter',
|
||||
'PythonLinter',
|
||||
'RubyLinter',
|
||||
'linter',
|
||||
'persist',
|
||||
'util',
|
||||
]
|
||||
449
Packages/SublimeLinter/lint/highlight.py
Normal file
@@ -0,0 +1,449 @@
|
||||
#
|
||||
# highlight.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Ryan Hileman and Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""
|
||||
This module implements highlighting code with marks.
|
||||
|
||||
The following classes are exported:
|
||||
|
||||
HighlightSet
|
||||
Highlight
|
||||
|
||||
|
||||
The following constants are exported:
|
||||
|
||||
WARNING - name of warning type
|
||||
ERROR - name of error type
|
||||
|
||||
MARK_KEY_FORMAT - format string for key used to mark code regions
|
||||
GUTTER_MARK_KEY_FORMAT - format string for key used to mark gutter mark regions
|
||||
MARK_SCOPE_FORMAT - format string used for color scheme scope names
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
import sublime
|
||||
from . import persist
|
||||
|
||||
#
|
||||
# Error types
|
||||
#
|
||||
WARNING = 'warning'
|
||||
ERROR = 'error'
|
||||
|
||||
MARK_KEY_FORMAT = 'sublimelinter-{}-marks'
|
||||
GUTTER_MARK_KEY_FORMAT = 'sublimelinter-{}-gutter-marks'
|
||||
MARK_SCOPE_FORMAT = 'sublimelinter.mark.{}'
|
||||
|
||||
UNDERLINE_FLAGS = sublime.DRAW_NO_FILL | sublime.DRAW_NO_OUTLINE | sublime.DRAW_EMPTY_AS_OVERWRITE
|
||||
|
||||
MARK_STYLES = {
|
||||
'outline': sublime.DRAW_NO_FILL,
|
||||
'fill': sublime.DRAW_NO_OUTLINE,
|
||||
'solid underline': sublime.DRAW_SOLID_UNDERLINE | UNDERLINE_FLAGS,
|
||||
'squiggly underline': sublime.DRAW_SQUIGGLY_UNDERLINE | UNDERLINE_FLAGS,
|
||||
'stippled underline': sublime.DRAW_STIPPLED_UNDERLINE | UNDERLINE_FLAGS,
|
||||
'none': sublime.HIDDEN
|
||||
}
|
||||
|
||||
WORD_RE = re.compile(r'^([-\w]+)')
|
||||
NEAR_RE_TEMPLATE = r'(?<!"){}({}){}(?!")'
|
||||
|
||||
|
||||
def mark_style_names():
|
||||
"""Return the keys from MARK_STYLES, sorted and capitalized, with None at the end."""
|
||||
names = list(MARK_STYLES)
|
||||
names.remove('none')
|
||||
names.sort()
|
||||
names.append('none')
|
||||
return [name.capitalize() for name in names]
|
||||
|
||||
|
||||
class HighlightSet:
|
||||
|
||||
"""This class maintains a set of Highlight objects and performs bulk operations on them."""
|
||||
|
||||
def __init__(self):
|
||||
self.all = set()
|
||||
|
||||
def add(self, highlight):
|
||||
"""Add a Highlight to the set."""
|
||||
self.all.add(highlight)
|
||||
|
||||
def draw(self, view):
|
||||
"""
|
||||
Draw all of the Highlight objects in our set.
|
||||
|
||||
Rather than draw each Highlight object individually, the marks in each
|
||||
object are aggregated into a new Highlight object, and that object
|
||||
is then drawn for the given view.
|
||||
|
||||
"""
|
||||
|
||||
if not self.all:
|
||||
return
|
||||
|
||||
all = Highlight()
|
||||
|
||||
for highlight in self.all:
|
||||
all.update(highlight)
|
||||
|
||||
all.draw(view)
|
||||
|
||||
@staticmethod
|
||||
def clear(view):
|
||||
"""Clear all marks in the given view."""
|
||||
for error_type in (WARNING, ERROR):
|
||||
view.erase_regions(MARK_KEY_FORMAT.format(error_type))
|
||||
view.erase_regions(GUTTER_MARK_KEY_FORMAT.format(error_type))
|
||||
|
||||
def redraw(self, view):
|
||||
"""Redraw all marks in the given view."""
|
||||
self.clear(view)
|
||||
self.draw(view)
|
||||
|
||||
def reset(self, view):
|
||||
"""Clear all marks in the given view and reset the list of marks in our Highlights."""
|
||||
self.clear(view)
|
||||
|
||||
for highlight in self.all:
|
||||
highlight.reset()
|
||||
|
||||
|
||||
class Highlight:
|
||||
|
||||
"""This class maintains error marks and knows how to draw them."""
|
||||
|
||||
def __init__(self, code=''):
|
||||
self.code = code
|
||||
self.marks = {WARNING: [], ERROR: []}
|
||||
self.mark_style = 'outline'
|
||||
self.mark_flags = MARK_STYLES[self.mark_style]
|
||||
|
||||
# Every line that has a mark is kept in this dict, so we know which
|
||||
# lines to mark in the gutter.
|
||||
self.lines = {}
|
||||
|
||||
# These are used when highlighting embedded code, for example JavaScript
|
||||
# or CSS within an HTML file. The embedded code is linted as if it begins
|
||||
# at (0, 0), but we need to keep track of where the actual start is within the source.
|
||||
self.line_offset = 0
|
||||
self.char_offset = 0
|
||||
|
||||
# Linting runs asynchronously on a snapshot of the code. Marks are added to the code
|
||||
# during that asynchronous linting, and the markup code needs to calculate character
|
||||
# positions given a line + column. By the time marks are added, the actual buffer
|
||||
# may have changed, so we can't reliably use the plugin API to calculate character
|
||||
# positions. The solution is to calculate and store the character positions for
|
||||
# every line when this object is created, then reference that when needed.
|
||||
self.newlines = newlines = [0]
|
||||
last = -1
|
||||
|
||||
while True:
|
||||
last = code.find('\n', last + 1)
|
||||
|
||||
if last == -1:
|
||||
break
|
||||
|
||||
newlines.append(last + 1)
|
||||
|
||||
newlines.append(len(code))
|
||||
|
||||
@staticmethod
|
||||
def strip_quotes(text):
|
||||
"""Return text stripped of enclosing single/double quotes."""
|
||||
first = text[0]
|
||||
|
||||
if first in ('\'', '"') and text[-1] == first:
|
||||
text = text[1:-1]
|
||||
|
||||
return text
|
||||
|
||||
def full_line(self, line):
|
||||
"""
|
||||
Return the start/end character positions for the given line.
|
||||
|
||||
This returns *real* character positions (relative to the beginning of self.code)
|
||||
base on the *virtual* line number (adjusted by the self.line_offset).
|
||||
|
||||
"""
|
||||
|
||||
# The first line of the code needs the character offset
|
||||
if line == 0:
|
||||
char_offset = self.char_offset
|
||||
else:
|
||||
char_offset = 0
|
||||
|
||||
line += self.line_offset
|
||||
start = self.newlines[line] + char_offset
|
||||
|
||||
end = self.newlines[min(line + 1, len(self.newlines) - 1)]
|
||||
|
||||
return start, end
|
||||
|
||||
def range(self, line, pos, length=-1, near=None, error_type=ERROR, word_re=None):
|
||||
"""
|
||||
Mark a range of text.
|
||||
|
||||
line and pos should be zero-based. The pos and length argument can be used to control marking:
|
||||
|
||||
- If pos < 0, the entire line is marked and length is ignored.
|
||||
|
||||
- If near is not None, it is stripped of quotes and length = len(near)
|
||||
|
||||
- If length < 0, the nearest word starting at pos is marked, and if
|
||||
no word is matched, the character at pos is marked.
|
||||
|
||||
- If length == 0, no text is marked, but a gutter mark will appear on that line.
|
||||
|
||||
error_type determines what type of error mark will be drawn (ERROR or WARNING).
|
||||
|
||||
When length < 0, this method attempts to mark the closest word at pos on the given line.
|
||||
If you want to customize the word matching regex, pass it in word_re.
|
||||
|
||||
If the error_type is WARNING and an identical ERROR region exists, it is not added.
|
||||
If the error_type is ERROR and an identical WARNING region exists, the warning region
|
||||
is removed and the error region is added.
|
||||
|
||||
"""
|
||||
|
||||
start, end = self.full_line(line)
|
||||
|
||||
if pos < 0:
|
||||
pos = 0
|
||||
length = (end - start) - 1
|
||||
elif near is not None:
|
||||
near = self.strip_quotes(near)
|
||||
length = len(near)
|
||||
elif length < 0:
|
||||
code = self.code[start:end][pos:]
|
||||
match = (word_re or WORD_RE).search(code)
|
||||
|
||||
if match:
|
||||
length = len(match.group())
|
||||
else:
|
||||
length = 1
|
||||
|
||||
pos += start
|
||||
region = sublime.Region(pos, pos + length)
|
||||
other_type = ERROR if error_type == WARNING else WARNING
|
||||
i_offset = 0
|
||||
|
||||
for i, mark in enumerate(self.marks[other_type].copy()):
|
||||
if mark.a == region.a and mark.b == region.b:
|
||||
if error_type == WARNING:
|
||||
return
|
||||
else:
|
||||
self.marks[other_type].pop(i - i_offset)
|
||||
i_offset += 1
|
||||
|
||||
self.marks[error_type].append(region)
|
||||
|
||||
def regex(self, line, regex, error_type=ERROR,
|
||||
line_match=None, word_match=None, word_re=None):
|
||||
"""
|
||||
Mark a range of text that matches a regex.
|
||||
|
||||
line, error_type and word_re are the same as in range().
|
||||
|
||||
line_match may be a string pattern or a compiled regex.
|
||||
If provided, it must have a named group called 'match' that
|
||||
determines which part of the source line will be considered
|
||||
for marking.
|
||||
|
||||
word_match may be a string pattern or a compiled regex.
|
||||
If provided, it must have a named group called 'mark' that
|
||||
determines which part of the source line will actually be marked.
|
||||
Multiple portions of the source line may match.
|
||||
|
||||
"""
|
||||
|
||||
offset = 0
|
||||
|
||||
start, end = self.full_line(line)
|
||||
line_text = self.code[start:end]
|
||||
|
||||
if line_match:
|
||||
match = re.match(line_match, line_text)
|
||||
|
||||
if match:
|
||||
line_text = match.group('match')
|
||||
offset = match.start('match')
|
||||
else:
|
||||
return
|
||||
|
||||
it = re.finditer(regex, line_text)
|
||||
results = [
|
||||
result.span('mark')
|
||||
for result in it
|
||||
if word_match is None or result.group('mark') == word_match
|
||||
]
|
||||
|
||||
for start, end in results:
|
||||
self.range(line, start + offset, end - start, error_type=error_type)
|
||||
|
||||
def near(self, line, near, error_type=ERROR, word_re=None):
|
||||
"""
|
||||
Mark a range of text near a given word.
|
||||
|
||||
line, error_type and word_re are the same as in range().
|
||||
|
||||
If near is enclosed by quotes, they are stripped. The first occurrence
|
||||
of near in the given line of code is matched. If the first and last
|
||||
characters of near are word characters, a match occurs only if near
|
||||
is a complete word.
|
||||
|
||||
The position at which near is found is returned, or zero if there
|
||||
is no match.
|
||||
|
||||
"""
|
||||
|
||||
if not near:
|
||||
return
|
||||
|
||||
start, end = self.full_line(line)
|
||||
text = self.code[start:end]
|
||||
near = self.strip_quotes(near)
|
||||
|
||||
# Add \b fences around the text if it begins/ends with a word character
|
||||
fence = ['', '']
|
||||
|
||||
for i, pos in enumerate((0, -1)):
|
||||
if near[pos].isalnum() or near[pos] == '_':
|
||||
fence[i] = r'\b'
|
||||
|
||||
pattern = NEAR_RE_TEMPLATE.format(fence[0], re.escape(near), fence[1])
|
||||
match = re.search(pattern, text)
|
||||
|
||||
if match:
|
||||
start = match.start(1)
|
||||
else:
|
||||
start = -1
|
||||
|
||||
if start != -1:
|
||||
self.range(line, start, len(near), error_type=error_type, word_re=word_re)
|
||||
return start
|
||||
else:
|
||||
return 0
|
||||
|
||||
def update(self, other):
|
||||
"""
|
||||
Update this object with another Highlight.
|
||||
|
||||
It is assumed that other.code == self.code.
|
||||
|
||||
other's marks and error positions are merged, and this
|
||||
object takes the newlines array from other.
|
||||
|
||||
"""
|
||||
|
||||
for error_type in (WARNING, ERROR):
|
||||
self.marks[error_type].extend(other.marks[error_type])
|
||||
|
||||
# Errors override warnings on the same line
|
||||
for line, error_type in other.lines.items():
|
||||
current_type = self.lines.get(line)
|
||||
|
||||
if current_type is None or current_type == WARNING:
|
||||
self.lines[line] = error_type
|
||||
|
||||
self.newlines = other.newlines
|
||||
|
||||
def set_mark_style(self):
|
||||
"""Setup the mark style and flags based on settings."""
|
||||
self.mark_style = persist.settings.get('mark_style', 'outline')
|
||||
self.mark_flags = MARK_STYLES[self.mark_style]
|
||||
|
||||
if not persist.settings.get('show_marks_in_minimap'):
|
||||
self.mark_flags |= sublime.HIDE_ON_MINIMAP
|
||||
|
||||
def draw(self, view):
|
||||
"""
|
||||
Draw code and gutter marks in the given view.
|
||||
|
||||
Error, warning and gutter marks are drawn with separate regions,
|
||||
since each one potentially needs a different color.
|
||||
|
||||
"""
|
||||
self.set_mark_style()
|
||||
|
||||
gutter_regions = {WARNING: [], ERROR: []}
|
||||
draw_gutter_marks = persist.settings.get('gutter_theme') != 'None'
|
||||
|
||||
if draw_gutter_marks:
|
||||
# We use separate regions for the gutter marks so we can use
|
||||
# a scope that will not colorize the gutter icon, and to ensure
|
||||
# that errors will override warnings.
|
||||
for line, error_type in self.lines.items():
|
||||
region = sublime.Region(self.newlines[line], self.newlines[line])
|
||||
gutter_regions[error_type].append(region)
|
||||
|
||||
for error_type in (WARNING, ERROR):
|
||||
if self.marks[error_type]:
|
||||
view.add_regions(
|
||||
MARK_KEY_FORMAT.format(error_type),
|
||||
self.marks[error_type],
|
||||
MARK_SCOPE_FORMAT.format(error_type),
|
||||
flags=self.mark_flags
|
||||
)
|
||||
|
||||
if draw_gutter_marks and gutter_regions[error_type]:
|
||||
if persist.gutter_marks['colorize']:
|
||||
scope = MARK_SCOPE_FORMAT.format(error_type)
|
||||
else:
|
||||
scope = 'sublimelinter.gutter-mark'
|
||||
|
||||
view.add_regions(
|
||||
GUTTER_MARK_KEY_FORMAT.format(error_type),
|
||||
gutter_regions[error_type],
|
||||
scope,
|
||||
icon=persist.gutter_marks[error_type]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def clear(view):
|
||||
"""Clear all marks in the given view."""
|
||||
for error_type in (WARNING, ERROR):
|
||||
view.erase_regions(MARK_KEY_FORMAT.format(error_type))
|
||||
view.erase_regions(GUTTER_MARK_KEY_FORMAT.format(error_type))
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Clear the list of marks maintained by this object.
|
||||
|
||||
This method does not clear the marks, only the list.
|
||||
The next time this object is used to draw, the marks will be cleared.
|
||||
|
||||
"""
|
||||
for error_type in (WARNING, ERROR):
|
||||
del self.marks[error_type][:]
|
||||
self.lines.clear()
|
||||
|
||||
def line(self, line, error_type):
|
||||
"""Record the given line as having the given error type."""
|
||||
line += self.line_offset
|
||||
|
||||
# Errors override warnings, if it's already an error leave it
|
||||
if self.lines.get(line) == ERROR:
|
||||
return
|
||||
|
||||
self.lines[line] = error_type
|
||||
|
||||
def move_to(self, line, char_offset):
|
||||
"""
|
||||
Move the highlight to the given line and character offset.
|
||||
|
||||
The character offset is relative to the start of the line.
|
||||
This method is used to create virtual line numbers
|
||||
and character positions when linting embedded code.
|
||||
|
||||
"""
|
||||
self.line_offset = line
|
||||
self.char_offset = char_offset
|
||||
1673
Packages/SublimeLinter/lint/linter.py
Normal file
465
Packages/SublimeLinter/lint/persist.py
Normal file
@@ -0,0 +1,465 @@
|
||||
#
|
||||
# persist.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Ryan Hileman and Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module provides persistent global storage for other modules."""
|
||||
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sublime
|
||||
import sys
|
||||
|
||||
from . import util
|
||||
|
||||
PLUGIN_NAME = 'SublimeLinter'
|
||||
|
||||
# Get the name of the plugin directory, which is the parent of this file's directory
|
||||
PLUGIN_DIRECTORY = os.path.basename(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
LINT_MODES = (
|
||||
('background', 'Lint whenever the text is modified'),
|
||||
('load/save', 'Lint only when a file is loaded or saved'),
|
||||
('save only', 'Lint only when a file is saved'),
|
||||
('manual', 'Lint only when requested')
|
||||
)
|
||||
|
||||
SYNTAX_RE = re.compile(r'(?i)/([^/]+)\.tmLanguage$')
|
||||
|
||||
DEFAULT_GUTTER_THEME_PATH = 'Packages/SublimeLinter/gutter-themes/Default/Default.gutter-theme'
|
||||
|
||||
|
||||
class Settings:
|
||||
|
||||
"""This class provides global access to and management of plugin settings."""
|
||||
|
||||
def __init__(self):
|
||||
self.settings = {}
|
||||
self.previous_settings = {}
|
||||
self.changeset = set()
|
||||
self.plugin_settings = None
|
||||
self.on_update_callback = None
|
||||
|
||||
def load(self, force=False):
|
||||
"""Load the plugin settings."""
|
||||
if force or not self.settings:
|
||||
self.observe()
|
||||
self.on_update()
|
||||
self.observe_prefs()
|
||||
|
||||
def has_setting(self, setting):
|
||||
"""Return whether the given setting exists."""
|
||||
return setting in self.settings
|
||||
|
||||
def get(self, setting, default=None):
|
||||
"""Return a plugin setting, defaulting to default if not found."""
|
||||
return self.settings.get(setting, default)
|
||||
|
||||
def set(self, setting, value, changed=False):
|
||||
"""
|
||||
Set a plugin setting to the given value.
|
||||
|
||||
Clients of this module should always call this method to set a value
|
||||
instead of doing settings['foo'] = 'bar'.
|
||||
|
||||
If the caller knows for certain that the value has changed,
|
||||
they should pass changed=True.
|
||||
|
||||
"""
|
||||
self.copy()
|
||||
self.settings[setting] = value
|
||||
|
||||
if changed:
|
||||
self.changeset.add(setting)
|
||||
|
||||
def pop(self, setting, default=None):
|
||||
"""
|
||||
Remove a given setting and return default if it is not in self.settings.
|
||||
|
||||
Clients of this module should always call this method to pop a value
|
||||
instead of doing settings.pop('foo').
|
||||
|
||||
"""
|
||||
self.copy()
|
||||
return self.settings.pop(setting, default)
|
||||
|
||||
def copy(self):
|
||||
"""Save a copy of the plugin settings."""
|
||||
self.previous_settings = deepcopy(self.settings)
|
||||
|
||||
def observe_prefs(self, observer=None):
|
||||
"""Observe changes to the ST prefs."""
|
||||
prefs = sublime.load_settings('Preferences.sublime-settings')
|
||||
prefs.clear_on_change('sublimelinter-pref-settings')
|
||||
prefs.add_on_change('sublimelinter-pref-settings', observer or self.on_prefs_update)
|
||||
|
||||
def observe(self, observer=None):
|
||||
"""Observer changes to the plugin settings."""
|
||||
self.plugin_settings = sublime.load_settings('SublimeLinter.sublime-settings')
|
||||
self.plugin_settings.clear_on_change('sublimelinter-persist-settings')
|
||||
self.plugin_settings.add_on_change('sublimelinter-persist-settings',
|
||||
observer or self.on_update)
|
||||
|
||||
def on_update_call(self, callback):
|
||||
"""Set a callback to call when user settings are updated."""
|
||||
self.on_update_callback = callback
|
||||
|
||||
def on_update(self):
|
||||
"""
|
||||
Update state when the user settings change.
|
||||
|
||||
The settings before the change are compared with the new settings.
|
||||
Depending on what changes, views will either be redrawn or relinted.
|
||||
|
||||
"""
|
||||
|
||||
settings = util.merge_user_settings(self.plugin_settings)
|
||||
self.settings.clear()
|
||||
self.settings.update(settings)
|
||||
|
||||
if (
|
||||
'@disable' in self.changeset or
|
||||
self.previous_settings.get('@disable', False) != self.settings.get('@disable', False)
|
||||
):
|
||||
need_relint = True
|
||||
self.changeset.discard('@disable')
|
||||
else:
|
||||
need_relint = False
|
||||
|
||||
# Clear the path-related caches if the paths list has changed
|
||||
if (
|
||||
'paths' in self.changeset or
|
||||
(self.previous_settings and
|
||||
self.previous_settings.get('paths') != self.settings.get('paths'))
|
||||
):
|
||||
need_relint = True
|
||||
util.clear_caches()
|
||||
self.changeset.discard('paths')
|
||||
|
||||
# Add python paths if they changed
|
||||
if (
|
||||
'python_paths' in self.changeset or
|
||||
(self.previous_settings and
|
||||
self.previous_settings.get('python_paths') != self.settings.get('python_paths'))
|
||||
):
|
||||
need_relint = True
|
||||
self.changeset.discard('python_paths')
|
||||
python_paths = self.settings.get('python_paths', {}).get(sublime.platform(), [])
|
||||
|
||||
for path in python_paths:
|
||||
if path not in sys.path:
|
||||
sys.path.append(path)
|
||||
|
||||
# If the syntax map changed, reassign linters to all views
|
||||
from .linter import Linter
|
||||
|
||||
if (
|
||||
'syntax_map' in self.changeset or
|
||||
(self.previous_settings and
|
||||
self.previous_settings.get('syntax_map') != self.settings.get('syntax_map'))
|
||||
):
|
||||
need_relint = True
|
||||
self.changeset.discard('syntax_map')
|
||||
Linter.clear_all()
|
||||
util.apply_to_all_views(lambda view: Linter.assign(view, reset=True))
|
||||
|
||||
if (
|
||||
'no_column_highlights_line' in self.changeset or
|
||||
self.previous_settings.get('no_column_highlights_line') != self.settings.get('no_column_highlights_line')
|
||||
):
|
||||
need_relint = True
|
||||
self.changeset.discard('no_column_highlights_line')
|
||||
|
||||
if (
|
||||
'gutter_theme' in self.changeset or
|
||||
self.previous_settings.get('gutter_theme') != self.settings.get('gutter_theme')
|
||||
):
|
||||
self.changeset.discard('gutter_theme')
|
||||
self.update_gutter_marks()
|
||||
|
||||
error_color = self.settings.get('error_color', '')
|
||||
warning_color = self.settings.get('warning_color', '')
|
||||
|
||||
if (
|
||||
('error_color' in self.changeset or 'warning_color' in self.changeset) or
|
||||
(self.previous_settings and error_color and warning_color and
|
||||
(self.previous_settings.get('error_color') != error_color or
|
||||
self.previous_settings.get('warning_color') != warning_color))
|
||||
):
|
||||
self.changeset.discard('error_color')
|
||||
self.changeset.discard('warning_color')
|
||||
|
||||
if (
|
||||
sublime.ok_cancel_dialog(
|
||||
'You changed the error and/or warning color. '
|
||||
'Would you like to update the user color schemes '
|
||||
'with the new colors?')
|
||||
):
|
||||
util.change_mark_colors(error_color, warning_color)
|
||||
|
||||
# If any other settings changed, relint
|
||||
if (self.previous_settings or len(self.changeset) > 0):
|
||||
need_relint = True
|
||||
|
||||
self.changeset.clear()
|
||||
|
||||
if need_relint:
|
||||
Linter.reload()
|
||||
|
||||
if self.previous_settings and self.on_update_callback:
|
||||
self.on_update_callback(need_relint)
|
||||
|
||||
def save(self, view=None):
|
||||
"""
|
||||
Regenerate and save the user settings.
|
||||
|
||||
User settings are updated with the default settings and the defaults
|
||||
from every linter, and if the user settings are currently being edited,
|
||||
the view is updated.
|
||||
|
||||
"""
|
||||
|
||||
self.load()
|
||||
|
||||
# Fill in default linter settings
|
||||
settings = self.settings
|
||||
linters = settings.pop('linters', {})
|
||||
|
||||
for name, linter in linter_classes.items():
|
||||
default = linter.settings().copy()
|
||||
default.update(linters.pop(name, {}))
|
||||
|
||||
for key, value in (('@disable', False), ('args', []), ('excludes', [])):
|
||||
if key not in default:
|
||||
default[key] = value
|
||||
|
||||
linters[name] = default
|
||||
|
||||
settings['linters'] = linters
|
||||
|
||||
filename = '{}.sublime-settings'.format(PLUGIN_NAME)
|
||||
user_prefs_path = os.path.join(sublime.packages_path(), 'User', filename)
|
||||
settings_views = []
|
||||
|
||||
if view is None:
|
||||
# See if any open views are the user prefs
|
||||
for window in sublime.windows():
|
||||
for view in window.views():
|
||||
if view.file_name() == user_prefs_path:
|
||||
settings_views.append(view)
|
||||
else:
|
||||
settings_views = [view]
|
||||
|
||||
if settings_views:
|
||||
def replace(edit):
|
||||
if not view.is_dirty():
|
||||
j = json.dumps({'user': settings}, indent=4, sort_keys=True)
|
||||
j = j.replace(' \n', '\n')
|
||||
view.replace(edit, sublime.Region(0, view.size()), j)
|
||||
|
||||
for view in settings_views:
|
||||
edits[view.id()].append(replace)
|
||||
view.run_command('sublimelinter_edit')
|
||||
view.run_command('save')
|
||||
else:
|
||||
user_settings = sublime.load_settings('SublimeLinter.sublime-settings')
|
||||
user_settings.set('user', settings)
|
||||
sublime.save_settings('SublimeLinter.sublime-settings')
|
||||
|
||||
def on_prefs_update(self):
|
||||
"""Perform maintenance when the ST prefs are updated."""
|
||||
util.generate_color_scheme()
|
||||
|
||||
def update_gutter_marks(self):
|
||||
"""Update the gutter mark info based on the the current "gutter_theme" setting."""
|
||||
|
||||
theme_path = self.settings.get('gutter_theme', DEFAULT_GUTTER_THEME_PATH)
|
||||
theme = os.path.splitext(os.path.basename(theme_path))[0]
|
||||
|
||||
if theme_path.lower() == 'none':
|
||||
gutter_marks['warning'] = gutter_marks['error'] = ''
|
||||
return
|
||||
|
||||
info = None
|
||||
|
||||
for path in (theme_path, DEFAULT_GUTTER_THEME_PATH):
|
||||
try:
|
||||
info = sublime.load_resource(path)
|
||||
break
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
if info is not None:
|
||||
if theme != 'Default' and os.path.basename(path) == 'Default.gutter-theme':
|
||||
printf('cannot find the gutter theme \'{}\', using the default'.format(theme))
|
||||
|
||||
path = os.path.dirname(path)
|
||||
|
||||
for error_type in ('warning', 'error'):
|
||||
icon_path = '{}/{}.png'.format(path, error_type)
|
||||
gutter_marks[error_type] = icon_path
|
||||
|
||||
try:
|
||||
info = json.loads(info)
|
||||
colorize = info.get('colorize', False)
|
||||
except ValueError:
|
||||
colorize = False
|
||||
|
||||
gutter_marks['colorize'] = colorize
|
||||
else:
|
||||
sublime.error_message(
|
||||
'SublimeLinter: cannot find the gutter theme "{}",'
|
||||
' and the default is also not available. '
|
||||
'No gutter marks will display.'.format(theme)
|
||||
)
|
||||
gutter_marks['warning'] = gutter_marks['error'] = ''
|
||||
|
||||
|
||||
if not 'queue' in globals():
|
||||
settings = Settings()
|
||||
|
||||
# A mapping between view ids and errors, which are line:(col, message) dicts
|
||||
errors = {}
|
||||
|
||||
# A mapping between view ids and HighlightSets
|
||||
highlights = {}
|
||||
|
||||
# A mapping between linter class names and linter classes
|
||||
linter_classes = {}
|
||||
|
||||
# A mapping between view ids and a set of linter instances
|
||||
view_linters = {}
|
||||
|
||||
# A mapping between view ids and views
|
||||
views = {}
|
||||
|
||||
# Every time a view is modified, this is updated with a mapping between a view id
|
||||
# and the time of the modification. This is checked at various stages of the linting
|
||||
# process. If a view has been modified since the original modification, the
|
||||
# linting process stops.
|
||||
last_hit_times = {}
|
||||
|
||||
edits = defaultdict(list)
|
||||
|
||||
# Info about the gutter mark icons
|
||||
gutter_marks = {'warning': 'Default', 'error': 'Default', 'colorize': True}
|
||||
|
||||
# Whether sys.path has been imported from the system.
|
||||
sys_path_imported = False
|
||||
|
||||
# Set to true when the plugin is loaded at startup
|
||||
plugin_is_loaded = False
|
||||
|
||||
|
||||
def get_syntax(view):
|
||||
"""Return the view's syntax or the syntax it is mapped to in the "syntax_map" setting."""
|
||||
view_syntax = view.settings().get('syntax', '')
|
||||
mapped_syntax = ''
|
||||
|
||||
if view_syntax:
|
||||
match = SYNTAX_RE.search(view_syntax)
|
||||
|
||||
if match:
|
||||
view_syntax = match.group(1).lower()
|
||||
mapped_syntax = settings.get('syntax_map', {}).get(view_syntax, '').lower()
|
||||
else:
|
||||
view_syntax = ''
|
||||
|
||||
return mapped_syntax or view_syntax
|
||||
|
||||
|
||||
def edit(vid, edit):
|
||||
"""Perform an operation on a view with the given edit object."""
|
||||
callbacks = edits.pop(vid, [])
|
||||
|
||||
for c in callbacks:
|
||||
c(edit)
|
||||
|
||||
|
||||
def view_did_close(vid):
|
||||
"""Remove all references to the given view id in persistent storage."""
|
||||
if vid in errors:
|
||||
del errors[vid]
|
||||
|
||||
if vid in highlights:
|
||||
del highlights[vid]
|
||||
|
||||
if vid in view_linters:
|
||||
del view_linters[vid]
|
||||
|
||||
if vid in views:
|
||||
del views[vid]
|
||||
|
||||
if vid in last_hit_times:
|
||||
del last_hit_times[vid]
|
||||
|
||||
|
||||
def debug_mode():
|
||||
"""Return whether the "debug" setting is True."""
|
||||
return settings.get('debug')
|
||||
|
||||
|
||||
def debug(*args):
|
||||
"""Print args to the console if the "debug" setting is True."""
|
||||
if settings.get('debug'):
|
||||
printf(*args)
|
||||
|
||||
|
||||
def printf(*args):
|
||||
"""Print args to the console, prefixed by the plugin name."""
|
||||
print(PLUGIN_NAME + ': ', end='')
|
||||
|
||||
for arg in args:
|
||||
print(arg, end=' ')
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def import_sys_path():
|
||||
"""Import system python 3 sys.path into our sys.path."""
|
||||
global sys_path_imported
|
||||
|
||||
if plugin_is_loaded and not sys_path_imported:
|
||||
# Make sure the system python 3 paths are available to plugins.
|
||||
# We do this here to ensure it is only done once.
|
||||
sys.path.extend(util.get_python_paths())
|
||||
sys_path_imported = True
|
||||
|
||||
|
||||
def register_linter(linter_class, name, attrs):
|
||||
"""Add a linter class to our mapping of class names <--> linter classes."""
|
||||
if name:
|
||||
name = name.lower()
|
||||
linter_classes[name] = linter_class
|
||||
|
||||
# By setting the lint_settings to None, they will be set the next
|
||||
# time linter_class.settings() is called.
|
||||
linter_class.lint_settings = None
|
||||
|
||||
# The sublime plugin API is not available until plugin_loaded is executed
|
||||
if plugin_is_loaded:
|
||||
settings.load(force=True)
|
||||
|
||||
# If a linter is reloaded, we have to reassign that linter to all views
|
||||
from . import linter
|
||||
|
||||
# If the linter had previously been loaded, just reassign that linter
|
||||
if name in linter_classes:
|
||||
linter_name = name
|
||||
else:
|
||||
linter_name = None
|
||||
|
||||
for view in views.values():
|
||||
linter.Linter.assign(view, linter_name=linter_name)
|
||||
|
||||
printf('{} linter reloaded'.format(name))
|
||||
else:
|
||||
printf('{} linter loaded'.format(name))
|
||||
325
Packages/SublimeLinter/lint/python_linter.py
Normal file
@@ -0,0 +1,325 @@
|
||||
#
|
||||
# python_linter.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module exports the PythonLinter subclass of Linter."""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import re
|
||||
|
||||
from . import linter, persist, util
|
||||
|
||||
|
||||
class PythonLinter(linter.Linter):
|
||||
|
||||
"""
|
||||
This Linter subclass provides python-specific functionality.
|
||||
|
||||
Linters that check python should inherit from this class.
|
||||
By doing so, they automatically get the following features:
|
||||
|
||||
- comment_re is defined correctly for python.
|
||||
|
||||
- A python shebang is returned as the @python:<version> meta setting.
|
||||
|
||||
- Execution directly via a module method or via an executable.
|
||||
|
||||
If the module attribute is defined and is successfully imported,
|
||||
whether it is used depends on the following algorithm:
|
||||
|
||||
- If the cmd attribute specifies @python and ST's python
|
||||
satisfies that version, the module will be used. Note that this
|
||||
check is done during class construction.
|
||||
|
||||
- If the check_version attribute is False, the module will be used
|
||||
because the module is not version-sensitive.
|
||||
|
||||
- If the "@python" setting is set and ST's python satisfies
|
||||
that version, the module will be used.
|
||||
|
||||
- Otherwise the executable will be used with the python specified
|
||||
in the "@python" setting, the cmd attribute, or the default system
|
||||
python.
|
||||
|
||||
"""
|
||||
|
||||
SHEBANG_RE = re.compile(r'\s*#!(?:(?:/[^/]+)*[/ ])?python(?P<version>\d(?:\.\d)?)')
|
||||
|
||||
comment_re = r'\s*#'
|
||||
|
||||
# If the linter wants to import a module and run a method directly,
|
||||
# it should set this attribute to the module name, suitable for passing
|
||||
# to importlib.import_module. During class construction, the named module
|
||||
# will be imported, and if successful, the attribute will be replaced
|
||||
# with the imported module.
|
||||
module = None
|
||||
|
||||
# Some python-based linters are version-sensitive, i.e. the python version
|
||||
# they are run with has to match the version of the code they lint.
|
||||
# If a linter is version-sensitive, this attribute should be set to True.
|
||||
check_version = False
|
||||
|
||||
@staticmethod
|
||||
def match_shebang(code):
|
||||
"""Convert and return a python shebang as a @python:<version> setting."""
|
||||
|
||||
match = PythonLinter.SHEBANG_RE.match(code)
|
||||
|
||||
if match:
|
||||
return '@python', match.group('version')
|
||||
else:
|
||||
return None
|
||||
|
||||
shebang_match = match_shebang
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
"""Perform class-level initialization."""
|
||||
|
||||
super().initialize()
|
||||
persist.import_sys_path()
|
||||
cls.import_module()
|
||||
|
||||
@classmethod
|
||||
def reinitialize(cls):
|
||||
"""Perform class-level initialization after plugins have been loaded at startup."""
|
||||
|
||||
# Be sure to clear _cmd so that import_module will re-import.
|
||||
if hasattr(cls, '_cmd'):
|
||||
delattr(cls, '_cmd')
|
||||
|
||||
cls.initialize()
|
||||
|
||||
@classmethod
|
||||
def import_module(cls):
|
||||
"""
|
||||
Attempt to import the configured module.
|
||||
|
||||
If it could not be imported, use the executable.
|
||||
|
||||
"""
|
||||
|
||||
if hasattr(cls, '_cmd'):
|
||||
return
|
||||
|
||||
module = getattr(cls, 'module', None)
|
||||
cls._cmd = None
|
||||
cmd = cls.cmd
|
||||
script = None
|
||||
|
||||
if isinstance(cls.cmd, (list, tuple)):
|
||||
cmd = cls.cmd[0]
|
||||
|
||||
if module is not None:
|
||||
try:
|
||||
module = importlib.import_module(module)
|
||||
persist.debug('{} imported {}'.format(cls.name, module))
|
||||
|
||||
# If the linter specifies a python version, check to see
|
||||
# if ST's python satisfies that version.
|
||||
if cmd and not callable(cmd):
|
||||
match = util.PYTHON_CMD_RE.match(cmd)
|
||||
|
||||
if match and match.group('version'):
|
||||
version, script = match.group('version', 'script')
|
||||
version = util.find_python(version=version, script=script, module=module)
|
||||
|
||||
# If we cannot find a python or script of the right version,
|
||||
# we cannot use the module.
|
||||
if version[0] is None or script and version[1] is None:
|
||||
module = None
|
||||
|
||||
except ImportError:
|
||||
message = '{}import of {} module in {} failed'
|
||||
|
||||
if cls.check_version:
|
||||
warning = 'WARNING: '
|
||||
message += ', linter will not work with python 3 code'
|
||||
else:
|
||||
warning = ''
|
||||
message += ', linter will not run using built in python'
|
||||
|
||||
persist.printf(message.format(warning, module, cls.name))
|
||||
module = None
|
||||
|
||||
except Exception as ex:
|
||||
persist.printf(
|
||||
'ERROR: unknown exception in {}: {}'
|
||||
.format(cls.name, str(ex))
|
||||
)
|
||||
module = None
|
||||
|
||||
# If no module was specified, or the module could not be imported,
|
||||
# or ST's python does not satisfy the version specified, see if
|
||||
# any version of python available satisfies the linter. If not,
|
||||
# set the cmd to '' to disable the linter.
|
||||
can_lint = True
|
||||
|
||||
if not module and cmd and not callable(cmd):
|
||||
match = util.PYTHON_CMD_RE.match(cmd)
|
||||
|
||||
if match and match.group('version'):
|
||||
can_lint = False
|
||||
version, script = match.group('version', 'script')
|
||||
version = util.find_python(version=version, script=script)
|
||||
|
||||
if version[0] is not None and (not script or version[1] is not None):
|
||||
can_lint = True
|
||||
|
||||
if can_lint:
|
||||
cls._cmd = cls.cmd
|
||||
|
||||
# If there is a module, setting cmd to None tells us to
|
||||
# use the check method.
|
||||
if module:
|
||||
cls.cmd = None
|
||||
else:
|
||||
persist.printf(
|
||||
'WARNING: {} deactivated, no available version of python{} satisfies {}'
|
||||
.format(
|
||||
cls.name,
|
||||
' or {}'.format(script) if script else '',
|
||||
cmd
|
||||
))
|
||||
|
||||
cls.disabled = True
|
||||
|
||||
cls.module = module
|
||||
|
||||
def context_sensitive_executable_path(self, cmd):
|
||||
"""
|
||||
Calculate the context-sensitive executable path, using @python and check_version.
|
||||
|
||||
Return a tuple of (have_path, path).
|
||||
|
||||
Return have_path == False if not self.check_version.
|
||||
Return have_path == True if cmd is in [script]@python[version] form.
|
||||
Return None for path if the desired version of python/script cannot be found.
|
||||
Return '<builtin>' for path if the built-in python should be used.
|
||||
|
||||
"""
|
||||
|
||||
if not self.check_version:
|
||||
return False, None
|
||||
|
||||
# Check to see if we have a @python command
|
||||
match = util.PYTHON_CMD_RE.match(cmd[0])
|
||||
|
||||
if match:
|
||||
settings = self.get_view_settings()
|
||||
|
||||
if '@python' in settings:
|
||||
script = match.group('script') or ''
|
||||
which = '{}@python{}'.format(script, settings.get('@python'))
|
||||
path = self.which(which)
|
||||
|
||||
if path:
|
||||
if path[0] == '<builtin>':
|
||||
return True, '<builtin>'
|
||||
elif path[0] is None or script and path[1] is None:
|
||||
return True, None
|
||||
|
||||
return True, path
|
||||
|
||||
return False, None
|
||||
|
||||
@classmethod
|
||||
def get_module_version(cls):
|
||||
"""
|
||||
Return the string version of the imported module, without any prefix/suffix.
|
||||
|
||||
This method handles the common case where a module (or one of its parents)
|
||||
defines a __version__ string. For other cases, subclasses should override
|
||||
this method and return the version string.
|
||||
|
||||
"""
|
||||
|
||||
if cls.module:
|
||||
module = cls.module
|
||||
|
||||
while True:
|
||||
if isinstance(getattr(module, '__version__', None), str):
|
||||
return module.__version__
|
||||
|
||||
if hasattr(module, '__package__'):
|
||||
try:
|
||||
module = importlib.import_module(module.__package__)
|
||||
except ImportError:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
def run(self, cmd, code):
|
||||
"""Run the module checker or executable on code and return the output."""
|
||||
if self.module is not None:
|
||||
use_module = False
|
||||
|
||||
if not self.check_version:
|
||||
use_module = True
|
||||
else:
|
||||
settings = self.get_view_settings()
|
||||
version = settings.get('@python')
|
||||
|
||||
if version is None:
|
||||
use_module = cmd is None or cmd[0] == '<builtin>'
|
||||
else:
|
||||
version = util.find_python(version=version, module=self.module)
|
||||
use_module = version[0] == '<builtin>'
|
||||
|
||||
if use_module:
|
||||
if persist.debug_mode():
|
||||
persist.printf(
|
||||
'{}: {} <builtin>'.format(
|
||||
self.name,
|
||||
os.path.basename(self.filename or '<unsaved>')
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
errors = self.check(code, os.path.basename(self.filename or '<unsaved>'))
|
||||
except Exception as err:
|
||||
persist.printf(
|
||||
'ERROR: exception in {}.check: {}'
|
||||
.format(self.name, str(err))
|
||||
)
|
||||
errors = ''
|
||||
|
||||
if isinstance(errors, (tuple, list)):
|
||||
return '\n'.join([str(e) for e in errors])
|
||||
else:
|
||||
return errors
|
||||
else:
|
||||
cmd = self._cmd
|
||||
else:
|
||||
cmd = self.cmd or self._cmd
|
||||
|
||||
cmd = self.build_cmd(cmd=cmd)
|
||||
|
||||
if cmd:
|
||||
return super().run(cmd, code)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def check(self, code, filename):
|
||||
"""
|
||||
Run a built-in check of code, returning errors.
|
||||
|
||||
Subclasses that provide built in checking must override this method
|
||||
and return a string with one more lines per error, an array of strings,
|
||||
or an array of objects that can be converted to strings.
|
||||
|
||||
"""
|
||||
|
||||
persist.printf(
|
||||
'{}: subclasses must override the PythonLinter.check method'
|
||||
.format(self.name)
|
||||
)
|
||||
|
||||
return ''
|
||||
134
Packages/SublimeLinter/lint/queue.py
Normal file
@@ -0,0 +1,134 @@
|
||||
#
|
||||
# queue.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Ryan Hileman and Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module provides a threaded queue for lint requests."""
|
||||
|
||||
from queue import Queue, Empty
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
from . import persist, util
|
||||
|
||||
|
||||
class Daemon:
|
||||
|
||||
"""
|
||||
This class provides a threaded queue that dispatches lints.
|
||||
|
||||
The following operations can be added to the queue:
|
||||
|
||||
hit - Queue a lint for a given view
|
||||
delay - Queue a delay for a number of milliseconds
|
||||
reload - Indicates the main plugin was reloaded
|
||||
|
||||
"""
|
||||
|
||||
MIN_DELAY = 0.1
|
||||
running = False
|
||||
callback = None
|
||||
q = Queue()
|
||||
last_runs = {}
|
||||
|
||||
def start(self, callback):
|
||||
"""Start the daemon thread that runs loop."""
|
||||
self.callback = callback
|
||||
|
||||
if self.running:
|
||||
self.q.put('reload')
|
||||
else:
|
||||
self.running = True
|
||||
threading.Thread(target=self.loop).start()
|
||||
|
||||
def loop(self):
|
||||
"""Continually check the queue for new items and process them."""
|
||||
|
||||
last_runs = {}
|
||||
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
item = self.q.get(block=True, timeout=self.MIN_DELAY)
|
||||
except Empty:
|
||||
for view_id, (timestamp, delay) in last_runs.copy().items():
|
||||
# Lint the view if we have gone past the time
|
||||
# at which the lint wants to run.
|
||||
if time.monotonic() > timestamp + delay:
|
||||
self.last_runs[view_id] = time.monotonic()
|
||||
del last_runs[view_id]
|
||||
self.lint(view_id, timestamp)
|
||||
|
||||
continue
|
||||
|
||||
if isinstance(item, tuple):
|
||||
view_id, timestamp, delay = item
|
||||
|
||||
if view_id in self.last_runs and timestamp < self.last_runs[view_id]:
|
||||
continue
|
||||
|
||||
last_runs[view_id] = timestamp, delay
|
||||
|
||||
elif isinstance(item, (int, float)):
|
||||
time.sleep(item)
|
||||
|
||||
elif isinstance(item, str):
|
||||
if item == 'reload':
|
||||
persist.printf('daemon detected a reload')
|
||||
self.last_runs.clear()
|
||||
last_runs.clear()
|
||||
else:
|
||||
persist.printf('unknown message sent to daemon:', item)
|
||||
except:
|
||||
persist.printf('error in SublimeLinter daemon:')
|
||||
persist.printf('-' * 20)
|
||||
persist.printf(traceback.format_exc())
|
||||
persist.printf('-' * 20)
|
||||
|
||||
def hit(self, view):
|
||||
"""Add a lint request to the queue, return the time at which the request was enqueued."""
|
||||
timestamp = time.monotonic()
|
||||
self.q.put((view.id(), timestamp, self.get_delay(view)))
|
||||
return timestamp
|
||||
|
||||
def delay(self, milliseconds=100):
|
||||
"""Add a millisecond delay to the queue."""
|
||||
self.q.put(milliseconds / 1000.0)
|
||||
|
||||
def lint(self, view_id, timestamp):
|
||||
"""
|
||||
Call back into the main plugin to lint the given view.
|
||||
|
||||
timestamp is used to determine if the view has been modified
|
||||
since the lint was requested.
|
||||
|
||||
"""
|
||||
self.callback(view_id, timestamp)
|
||||
|
||||
def get_delay(self, view):
|
||||
"""
|
||||
Return the delay between a lint request and when it will be processed.
|
||||
|
||||
If the lint mode is not background, there is no delay. Otherwise, if
|
||||
a "delay" setting is not available in any of the settings, MIN_DELAY is used.
|
||||
|
||||
"""
|
||||
|
||||
if persist.settings.get('lint_mode') != 'background':
|
||||
return 0
|
||||
|
||||
delay = (util.get_view_rc_settings(view) or {}).get('delay')
|
||||
|
||||
if delay is None:
|
||||
delay = persist.settings.get('delay', self.MIN_DELAY)
|
||||
|
||||
return delay
|
||||
|
||||
|
||||
queue = Daemon()
|
||||
144
Packages/SublimeLinter/lint/ruby_linter.py
Normal file
@@ -0,0 +1,144 @@
|
||||
#
|
||||
# ruby_linter.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module exports the RubyLinter subclass of Linter."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
|
||||
from . import linter, persist, util
|
||||
|
||||
CMD_RE = re.compile(r'(?P<gem>.+?)@ruby')
|
||||
|
||||
|
||||
class RubyLinter(linter.Linter):
|
||||
|
||||
"""
|
||||
This Linter subclass provides ruby-specific functionality.
|
||||
|
||||
Linters that check ruby using gems should inherit from this class.
|
||||
By doing so, they automatically get the following features:
|
||||
|
||||
- comment_re is defined correctly for ruby.
|
||||
|
||||
- Support for rbenv and rvm (via rvm-auto-ruby).
|
||||
|
||||
"""
|
||||
|
||||
comment_re = r'\s*#'
|
||||
|
||||
@classmethod
|
||||
def initialize(cls):
|
||||
"""Perform class-level initialization."""
|
||||
|
||||
super().initialize()
|
||||
|
||||
if cls.executable_path is not None:
|
||||
return
|
||||
|
||||
if not callable(cls.cmd) and cls.cmd:
|
||||
cls.executable_path = cls.lookup_executables(cls.cmd)
|
||||
elif cls.executable:
|
||||
cls.executable_path = cls.lookup_executables(cls.executable)
|
||||
|
||||
if not cls.executable_path:
|
||||
cls.disabled = True
|
||||
|
||||
@classmethod
|
||||
def reinitialize(cls):
|
||||
"""Perform class-level initialization after plugins have been loaded at startup."""
|
||||
|
||||
# Be sure to clear cls.executable_path so that lookup_executables will run.
|
||||
cls.executable_path = None
|
||||
cls.initialize()
|
||||
|
||||
@classmethod
|
||||
def lookup_executables(cls, cmd):
|
||||
"""
|
||||
Attempt to locate the gem and ruby specified in cmd, return new cmd list.
|
||||
|
||||
The following forms are valid:
|
||||
|
||||
gem@ruby
|
||||
gem
|
||||
ruby
|
||||
|
||||
If rbenv is installed and the gem is also under rbenv control,
|
||||
the gem will be executed directly. Otherwise [ruby <, gem>] will
|
||||
be returned.
|
||||
|
||||
If rvm-auto-ruby is installed, [rvm-auto-ruby <, gem>] will be
|
||||
returned.
|
||||
|
||||
Otherwise [ruby] or [gem] will be returned.
|
||||
|
||||
"""
|
||||
|
||||
ruby = None
|
||||
rbenv = util.which('rbenv')
|
||||
|
||||
if not rbenv:
|
||||
ruby = util.which('rvm-auto-ruby')
|
||||
|
||||
if not ruby:
|
||||
ruby = util.which('ruby')
|
||||
|
||||
if not rbenv and not ruby:
|
||||
persist.printf(
|
||||
'WARNING: {} deactivated, cannot locate ruby, rbenv or rvm-auto-ruby'
|
||||
.format(cls.name, cmd[0])
|
||||
)
|
||||
return []
|
||||
|
||||
if isinstance(cmd, str):
|
||||
cmd = shlex.split(cmd)
|
||||
|
||||
match = CMD_RE.match(cmd[0])
|
||||
|
||||
if match:
|
||||
gem = match.group('gem')
|
||||
elif cmd[0] != 'ruby':
|
||||
gem = cmd[0]
|
||||
else:
|
||||
gem = ''
|
||||
|
||||
if gem:
|
||||
gem_path = util.which(gem)
|
||||
|
||||
if gem_path:
|
||||
if (rbenv and
|
||||
('{0}.rbenv{0}shims{0}'.format(os.sep) in gem_path or
|
||||
(os.altsep and '{0}.rbenv{0}shims{0}'.format(os.altsep in gem_path)))):
|
||||
ruby_cmd = [gem_path]
|
||||
else:
|
||||
ruby_cmd = [ruby, gem_path]
|
||||
else:
|
||||
persist.printf(
|
||||
'WARNING: {} deactivated, cannot locate the gem \'{}\''
|
||||
.format(cls.name, gem)
|
||||
)
|
||||
return []
|
||||
else:
|
||||
ruby_cmd = [ruby]
|
||||
|
||||
if cls.env is None:
|
||||
# Don't use GEM_HOME with rbenv, it prevents it from using gem shims
|
||||
if rbenv:
|
||||
cls.env = {}
|
||||
else:
|
||||
gem_home = util.get_environment_variable('GEM_HOME')
|
||||
|
||||
if gem_home:
|
||||
cls.env = {'GEM_HOME': gem_home}
|
||||
else:
|
||||
cls.env = {}
|
||||
|
||||
return ruby_cmd
|
||||
1416
Packages/SublimeLinter/lint/util.py
Normal file
1
Packages/SublimeLinter/linter-plugin-template/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"@python": 3,
|
||||
"linters": {
|
||||
"flake8": {
|
||||
"max-line-length": 120
|
||||
},
|
||||
"pep8": {
|
||||
"max-line-length": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/SublimeLinter/linter-plugin-template/.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
language: python
|
||||
python:
|
||||
- "3.3"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- pip install flake8
|
||||
- pip install pep257
|
||||
# command to run tests
|
||||
script:
|
||||
- flake8 *.py --max-line-length=120
|
||||
- pep257 *.py
|
||||
17
Packages/SublimeLinter/linter-plugin-template/LICENSE
Normal file
@@ -0,0 +1,17 @@
|
||||
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.
|
||||
73
Packages/SublimeLinter/linter-plugin-template/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
SublimeLinter-contrib-__linter__
|
||||
================================
|
||||
|
||||
[](https://travis-ci.org/SublimeLinter/SublimeLinter-contrib-__linter__)
|
||||
|
||||
This linter plugin for [SublimeLinter][docs] provides an interface to [__linter__](__linter_homepage__). It will be used with files that have the “__syntax__” syntax.
|
||||
|
||||
## Installation
|
||||
SublimeLinter 3 must be installed in order to use this plugin. If SublimeLinter 3 is not installed, please follow the instructions [here][installation].
|
||||
|
||||
### Linter installation
|
||||
Before using this plugin, you must ensure that `__linter__` is installed on your system. To install `__linter__`, do the following:
|
||||
|
||||
1. Install __platform__.
|
||||
|
||||
1. Install `__linter__` by typing the following in a terminal:
|
||||
```
|
||||
__install__
|
||||
```
|
||||
__extra_install_steps__
|
||||
|
||||
**Note:** This plugin requires `__linter__` __version__ or later.
|
||||
|
||||
### Linter configuration
|
||||
In order for `__linter__` to be executed by SublimeLinter, you must ensure that its path is available to SublimeLinter. Before going any further, please read and follow the steps in [“Finding a linter executable”](http://sublimelinter.readthedocs.org/en/latest/troubleshooting.html#finding-a-linter-executable) through “Validating your PATH” in the documentation.
|
||||
|
||||
Once you have installed and configured `__linter__`, you can proceed to install the SublimeLinter-contrib-__linter__ plugin if it is not yet installed.
|
||||
|
||||
### Plugin installation
|
||||
Please use [Package Control][pc] to install the linter plugin. This will ensure that the plugin will be updated when new versions are available. If you want to install from source so you can modify the source code, you probably know what you are doing so we won’t cover that here.
|
||||
|
||||
To install via Package Control, do the following:
|
||||
|
||||
1. Within Sublime Text, bring up the [Command Palette][cmd] and type `install`. Among the commands you should see `Package Control: Install Package`. If that command is not highlighted, use the keyboard or mouse to select it. There will be a pause of a few seconds while Package Control fetches the list of available plugins.
|
||||
|
||||
1. When the plugin list appears, type `__linter__`. Among the entries you should see `SublimeLinter-contrib-__linter__`. If that entry is not highlighted, use the keyboard or mouse to select it.
|
||||
|
||||
## Settings
|
||||
For general information on how SublimeLinter works with settings, please see [Settings][settings]. For information on generic linter settings, please see [Linter Settings][linter-settings].
|
||||
|
||||
In addition to the standard SublimeLinter settings, SublimeLinter-contrib-__linter__ provides its own settings. Those marked as “Inline Setting” or “Inline Override” may also be [used inline][inline-settings].
|
||||
|
||||
|Setting|Description|Inline Setting|Inline Override|
|
||||
|:------|:----------|:------------:|:-------------:|
|
||||
|foo|Something.|✓| |
|
||||
|bar|Something else.| |✓|
|
||||
|
||||
## Contributing
|
||||
If you would like to contribute enhancements or fixes, please do the following:
|
||||
|
||||
1. Fork the plugin repository.
|
||||
1. Hack on a separate topic branch created from the latest `master`.
|
||||
1. Commit and push the topic branch.
|
||||
1. Make a pull request.
|
||||
1. Be patient. ;-)
|
||||
|
||||
Please note that modications should follow these coding guidelines:
|
||||
|
||||
- Indent is 4 spaces.
|
||||
- Code should pass flake8 and pep257 linters.
|
||||
- Vertical whitespace helps readability, don’t be afraid to use it.
|
||||
- Please use descriptive variable names, no abbrevations unless they are very well known.
|
||||
|
||||
Thank you for helping out!
|
||||
|
||||
[docs]: http://sublimelinter.readthedocs.org
|
||||
[installation]: http://sublimelinter.readthedocs.org/en/latest/installation.html
|
||||
[locating-executables]: http://sublimelinter.readthedocs.org/en/latest/usage.html#how-linter-executables-are-located
|
||||
[pc]: https://sublime.wbond.net/installation
|
||||
[cmd]: http://docs.sublimetext.info/en/sublime-text-3/extensibility/command_palette.html
|
||||
[settings]: http://sublimelinter.readthedocs.org/en/latest/settings.html
|
||||
[linter-settings]: http://sublimelinter.readthedocs.org/en/latest/linter_settings.html
|
||||
[inline-settings]: http://sublimelinter.readthedocs.org/en/latest/settings.html#inline-settings
|
||||
36
Packages/SublimeLinter/linter-plugin-template/linter.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# linter.py
|
||||
# Linter for SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by __user__
|
||||
# Copyright (c) __year__ __user__
|
||||
#
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module exports the __class__ plugin class."""
|
||||
|
||||
from SublimeLinter.lint import __superclass__, util
|
||||
|
||||
|
||||
class __class__(__superclass__):
|
||||
|
||||
"""Provides an interface to __linter__."""
|
||||
|
||||
syntax = ''
|
||||
cmd = '__cmd__'
|
||||
executable = None
|
||||
version_args = '--version'
|
||||
version_re = r'(?P<version>\d+\.\d+\.\d+)'
|
||||
version_requirement = '>= 1.0'
|
||||
regex = r''
|
||||
multiline = False
|
||||
line_col_base = (1, 1)
|
||||
tempfile_suffix = None
|
||||
error_stream = util.STREAM_BOTH
|
||||
selectors = {}
|
||||
word_re = None
|
||||
defaults = {}
|
||||
inline_settings = None
|
||||
inline_overrides = None
|
||||
__extra_attributes__
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"install": "messages/install.txt"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
SublimeLinter-contrib-__linter__
|
||||
-------------------------------
|
||||
This linter plugin for SublimeLinter provides an interface to __linter__.
|
||||
|
||||
** IMPORTANT! **
|
||||
|
||||
Before this plugin will activate, you *must*
|
||||
follow the installation instructions here:
|
||||
|
||||
https://github.com/__user__/SublimeLinter-contrib-__linter__
|
||||
25
Packages/SublimeLinter/messages.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"install": "messages/install.txt",
|
||||
"3.0.5": "messages/3.0.5.txt",
|
||||
"3.0.6": "messages/3.0.6.txt",
|
||||
"3.0.9": "messages/3.0.9.txt",
|
||||
"3.0.10+1": "messages/3.0.10.txt",
|
||||
"3.0.11": "messages/3.0.11.txt",
|
||||
"3.0.12": "messages/3.0.12.txt",
|
||||
"3.0.15": "messages/3.0.15.txt",
|
||||
"3.0.16": "messages/3.0.16.txt",
|
||||
"3.0.17": "messages/3.0.17.txt",
|
||||
"3.0.18": "messages/3.0.18.txt",
|
||||
"3.0.19": "messages/3.0.19.txt",
|
||||
"3.0.20": "messages/3.0.20.txt",
|
||||
"3.0.23": "messages/3.0.23.txt",
|
||||
"3.0.23+1": "messages/3.0.23+1.txt",
|
||||
"3.0.24": "messages/3.0.24.txt",
|
||||
"3.0.25": "messages/3.0.25.txt",
|
||||
"3.0.26": "messages/3.0.26.txt",
|
||||
"3.0.27": "messages/3.0.27.txt",
|
||||
"3.0.28": "messages/3.0.28.txt",
|
||||
"3.0.29": "messages/3.0.29.txt",
|
||||
"3.0.30": "messages/3.0.30.txt",
|
||||
"3.0.31": "messages/3.0.31.txt"
|
||||
}
|
||||
10
Packages/SublimeLinter/messages/3.0.10.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
SublimeLinter 3.0.10
|
||||
---------------------
|
||||
|
||||
- Linting unsaved files works correctly now.
|
||||
|
||||
- When a linter plugin is loaded successfully, the console will say "<linter> activated" instead of "linter enabled". In addition, if the linter is disabled in your settings at load time, it will say so in the activation message, to make it clear the linter is active but not currently enabled.
|
||||
|
||||
- Non-string values may be used for lists of values in linter settings.
|
||||
|
||||
- When loading the first python-based linter on Windows, a command prompt window briefly opened. This has been fixed.
|
||||
10
Packages/SublimeLinter/messages/3.0.11.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
SublimeLinter 3.0.11
|
||||
---------------------
|
||||
|
||||
- Support was added for ruby-based linters via a new RubyLinter class.
|
||||
This class makes ruby-based linters work with rbenv and (hopefully)
|
||||
rvm. It also preserves your GEM_HOME when running ruby-based linters.
|
||||
See the documentation for more info.
|
||||
|
||||
- Some linters use html entitities in the error messages, so html
|
||||
entities in error messages are now decoded.
|
||||
6
Packages/SublimeLinter/messages/3.0.12.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
SublimeLinter 3.0.12
|
||||
---------------------
|
||||
|
||||
- A new Linter method was added, get_user_args, to make it easy
|
||||
for linter plugins to get the "args" setting and be sure
|
||||
it's a list.
|
||||
22
Packages/SublimeLinter/messages/3.0.15.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
SublimeLinter 3.0.15
|
||||
---------------------
|
||||
|
||||
- A new method was added, persist.debug_mode, to make it easier
|
||||
for linter plugins to determine if the user has debug mode
|
||||
turned on. Returns True if "debug" setting is on.
|
||||
|
||||
- Linters can now declare themselves as universal by setting
|
||||
the syntax to '*'. '*' can also be used as a key in selectors.
|
||||
See the SublimeLinter-annotations plugin for an example.
|
||||
|
||||
- When specifying a list setting as a string, for example:
|
||||
|
||||
"annotations": {
|
||||
"warnings": "FOO, BAR",
|
||||
}
|
||||
|
||||
in non-inline setting you can now put whitespace around
|
||||
the separator. Note that space may not be used around the
|
||||
separator if the setting is inline.
|
||||
|
||||
- Fixed some problems related to unexpected ST3 behavior.
|
||||
7
Packages/SublimeLinter/messages/3.0.16.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
SublimeLinter 3.0.16
|
||||
---------------------
|
||||
|
||||
- Fixed crashes related to missing settings.
|
||||
|
||||
- If the PATH output from your shell cannot be parsed,
|
||||
an error message is printed to the console.
|
||||
14
Packages/SublimeLinter/messages/3.0.17.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
SublimeLinter 3.0.17
|
||||
--------------------
|
||||
|
||||
- Fixed possible hang if a long-running process was started
|
||||
in the shell startup files. (SublimeLinter3-#30)
|
||||
|
||||
- If unable to read the shell PATH, you are warned that
|
||||
linters will most likely not work.
|
||||
|
||||
- Re-added ability to use @ filename placeholder in the cmd
|
||||
when the linter accepts stdin. (SublimeLinter-#618)
|
||||
|
||||
- Fixed the character offset of highlights on the first
|
||||
line of a selector region. (annotations-#1)
|
||||
11
Packages/SublimeLinter/messages/3.0.18.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
SublimeLinter 3.0.18
|
||||
---------------------
|
||||
- Hopefully dealing correctly with gems under rbenv/rvm now.
|
||||
|
||||
- Added Linter.config_file attribute for automating config file
|
||||
lookup. See the Writing Linters docs for more info.
|
||||
|
||||
- @ may be used as a prefix for arguments in the Linter.defaults attribute.
|
||||
It suppresses output of the arg name, allowing you to use a named
|
||||
inline setting/override and only have the values passed in the
|
||||
linter command. See the Writing Linters docs for more info.
|
||||
8
Packages/SublimeLinter/messages/3.0.19.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
SublimeLinter 3.0.19
|
||||
---------------------
|
||||
- Previously, when a linter plugin name had a dash in it, for example
|
||||
SublimeLinter-foo-bar, the inline setting name was "foobar",
|
||||
which could lead to confusion. Now both "foobar" and "foo-bar"
|
||||
are recognized as inline setting names.
|
||||
|
||||
- Fixed a bug with argument generation in some linters.
|
||||
10
Packages/SublimeLinter/messages/3.0.20.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
SublimeLinter 3.0.20
|
||||
---------------------
|
||||
- When using the "Create Linter Plugin" command, an executable name
|
||||
with dashes will be converted into a camel-case class name. For
|
||||
example, scss-lint becomes ScssLint.
|
||||
|
||||
- Added .gitignore with .DS_Store to the linter plugin template.
|
||||
|
||||
- You may now define a linter as file-only by setting its tempfile_suffix
|
||||
to '-', which means that linter will only run when its view is not dirty.
|
||||
12
Packages/SublimeLinter/messages/3.0.23+1.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
******************
|
||||
* Exciting news! *
|
||||
******************
|
||||
|
||||
After another week of hard work, I'm very happy
|
||||
and excited to announce that the SublimeLinter 3
|
||||
documentation has gone to a much better place...
|
||||
|
||||
http://sublimelinter.readthedocs.org
|
||||
|
||||
It's indexed, searchable, mobile-ready, and
|
||||
beautiful. Read those docs!
|
||||
15
Packages/SublimeLinter/messages/3.0.23.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
SublimeLinter 3.0.23
|
||||
---------------------
|
||||
- If no gutter marks are showing and there is no column in
|
||||
an error message, the entire line is highlighted.
|
||||
|
||||
- When specifying inline settings, "SublimeLinter" is now
|
||||
case-insensitive, so for example both "[SublimeLinter flake8-ignore:E201]"
|
||||
and "[sublimelinter flake8-ignore:E201]" will work.
|
||||
|
||||
- If you are using a node-based linter (like jshint), nvm,
|
||||
zsh, and oh-my-zsh, do **not** install the nvm plugin for
|
||||
oh-my-zsh. It interferes with SublimeLinter’s ability to
|
||||
find the path to node.
|
||||
|
||||
- Fixed some bugs relating to default settings.
|
||||
13
Packages/SublimeLinter/messages/3.0.24.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
SublimeLinter 3.0.24
|
||||
---------------------
|
||||
- A new linter setting has been added, "ignore_match",
|
||||
which allows you to ignore errors based on the error message.
|
||||
For more information, please see the documentation:
|
||||
|
||||
http://sublimelinter.readthedocs.org/en/latest/linter_settings.html#ignore-match
|
||||
|
||||
- A Dash doc feed is now available so you can read the
|
||||
SublimeLinter docs within Dash and they will always stay
|
||||
up to date with the latest version.
|
||||
|
||||
dash-feed://https%3A//media.readthedocs.org/dash/sublimelinter/latest/sublimelinter.xml
|
||||
17
Packages/SublimeLinter/messages/3.0.25.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
SublimeLinter 3.0.25
|
||||
---------------------
|
||||
- If you have multiple views of the same buffer open, even in multiple
|
||||
windows, the highlights, gutter marks and status messages are kept in sync.
|
||||
|
||||
- On Mac OS X, a syntax file will load even if the capitalization of its
|
||||
filename extension is not ".tmLanguage". On other platforms it will not
|
||||
load. But since it at least works on Mac OS X, SublimeLinter will allow
|
||||
it when resolving syntaxes.
|
||||
|
||||
- Added CONTRIBUTING.md, so github issues will showing something about
|
||||
the guidelines for contributing.
|
||||
|
||||
- Linter.config_file now allows for an abitrary number of auxiliary
|
||||
search directories to be searched, and '~' is expanded. This allows
|
||||
for linters like jshint to provide the native functionality of
|
||||
searching the file hierarchy and the home directory.
|
||||
9
Packages/SublimeLinter/messages/3.0.26.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
SublimeLinter 3.0.26
|
||||
---------------------
|
||||
- I have switched back to .zshenv as the recommended place
|
||||
to put PATH augmentations for zsh. On Ubuntu, shells are
|
||||
not started as login shells by default in terminals, which
|
||||
prevented .zprofile from loading. If anyone has trouble
|
||||
using .zshenv, let me know.
|
||||
|
||||
- Added Retina icons for the Default and Circle gutter themes.
|
||||
5
Packages/SublimeLinter/messages/3.0.27.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
SublimeLinter 3.0.27
|
||||
---------------------
|
||||
- Added fixed HTML (Rails) syntax to correctly support ERB files.
|
||||
|
||||
- Fixed the path for the Default gutter theme.
|
||||
28
Packages/SublimeLinter/messages/3.0.28.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
SublimeLinter 3.0.28
|
||||
---------------------
|
||||
|
||||
I am very grateful to everyone who has created linter plugins and made
|
||||
the commitment to maintain and support them.
|
||||
|
||||
SublimeLinter is only as good as its linters, so I feel every linter
|
||||
released to the public should be up to the standards I am holding
|
||||
myself to with SublimeLinter. As a result, I have taken the following
|
||||
step:
|
||||
|
||||
- We have moved all of the SublimeLinter linter plugins to a private channel.
|
||||
In the future, any requests for publishing through Package Control will
|
||||
be vetted for basic correctness.
|
||||
|
||||
- Linter plugins that are not in the SublimeLinter org on github have
|
||||
been renamed to SublimeLinter-contrib-*, so users can know that these
|
||||
are user-contributed, vs. the "official" linters in the SublimeLinter
|
||||
org.
|
||||
|
||||
- When creating a linter plugin using the "Create Linter Plugin" command,
|
||||
the name is SublimeLinter-contrib-* name.
|
||||
|
||||
Of course I want as many linters as possible in the SublimeLinter org.
|
||||
Anyone who is willing to have their plugin vetted and is willing to
|
||||
maintain and support their linter will be welcome in the org.
|
||||
|
||||
Thank you for your support!
|
||||
3
Packages/SublimeLinter/messages/3.0.29.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
SublimeLinter 3.0.29
|
||||
---------------------
|
||||
- Debug mode is now logged at startup.
|
||||
7
Packages/SublimeLinter/messages/3.0.30.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
SublimeLinter 3.0.30
|
||||
---------------------
|
||||
- When module import fails, the log message is more specific about
|
||||
the consequences of that failure.
|
||||
|
||||
- Fixed the case where non-version-sensitive python linters would fail
|
||||
to lint when a python 3 module was not available.
|
||||
7
Packages/SublimeLinter/messages/3.0.31.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
SublimeLinter 3.0.31
|
||||
---------------------
|
||||
- When reading PATH from the shell, the default timeout has been increased
|
||||
to 10 seconds. If that is not sufficient, a new setting, "shell_timeout",
|
||||
has been added, which can be set to the desired amount of time in seconds.
|
||||
|
||||
- When searching for executables, ~ is expanded in PATH components.
|
||||
68
Packages/SublimeLinter/messages/3.0.5.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
SublimeLinter 3.0.5
|
||||
--------------------
|
||||
|
||||
*** IMPORTANT! ***
|
||||
|
||||
PLEASE read this entire message!
|
||||
|
||||
|
||||
*** BUG REPORTING ***
|
||||
|
||||
Before running for help, please take the time to read the Troubleshooting
|
||||
guide, which has been greatly expanded in this version.
|
||||
|
||||
http://sublimelinter.readthedocs.org/en/latest/troubleshooting.html
|
||||
|
||||
If you still have a problem, please use github issues only for verified bugs.
|
||||
If you have a general usage question, or are having trouble with installation
|
||||
or configuration, please use the SublimeLinter google group. That way everyone will
|
||||
benefit from the answer.
|
||||
|
||||
Even if you suspect there is a bug, it's better to ask on the group first
|
||||
and then file an issue on github only after it has been confirmed that it's a bug.
|
||||
|
||||
https://groups.google.com/forum/#!forum/sublimelinter
|
||||
|
||||
|
||||
*** HAVE YOU READ THE DOCS? ***
|
||||
|
||||
If not, you are probably missing out on a lot of great new features in
|
||||
SublimeLinter 3. At least read the Usage docs.
|
||||
|
||||
A searchable version of docs with a Table of Contents is in the works.
|
||||
|
||||
|
||||
*** KEYBOARD CHANGES ***
|
||||
|
||||
The Mac OS X keyboard equivalent for Show All Errors is now Control+Command+A.
|
||||
|
||||
|
||||
*** LIVE GUTTER THEME PREVIEW! ***
|
||||
|
||||
You can now preview gutter themes in the chooser! For more info, see:
|
||||
|
||||
http://sublimelinter.readthedocs.org/en/latest/gutter_themes.html#choosing-a-gutter-theme
|
||||
|
||||
|
||||
*** LINTER PLUGIN AUTHORS ***
|
||||
|
||||
If you have already written a linter plugin, thank you! Please note
|
||||
that the usage of error_stream has changed a bit, please read:
|
||||
|
||||
http://sublimelinter.readthedocs.org/en/latest/linter_attributes.html#error-stream
|
||||
|
||||
|
||||
*** KEEP SUBLIMELINTER ALIVE! ***
|
||||
|
||||
I have now spent two solid months on SublimeLinter, and there is still lots to do.
|
||||
|
||||
I am very grateful to everyone who has donated so far to support
|
||||
SublimeLinter. But many have not. If everyone who relies on SublimeLinter
|
||||
donated even a few dollars, I wouldn't have to worry. But the reality is
|
||||
that without more financial support, I simply cannot continue to provide
|
||||
the same level of support for SublimeLinter.
|
||||
|
||||
I'm sorry to have to beg for donations, but open source software is not free
|
||||
-- the developers usually end up paying for it!
|
||||
|
||||
Thank you for your support!
|
||||
10
Packages/SublimeLinter/messages/3.0.6.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
SublimeLinter 3.0.6
|
||||
--------------------
|
||||
|
||||
- Fixed a bug in setting mark style and lint mode
|
||||
through the Command Palette.
|
||||
|
||||
- Show All Errors moves the selection to the highlighted error
|
||||
as you move through the items.
|
||||
|
||||
- Live preview of mark styles when choosing from the Command Palette.
|
||||
8
Packages/SublimeLinter/messages/3.0.9.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
SublimeLinter 3.0.9
|
||||
--------------------
|
||||
|
||||
- There are new commands to toggle, enable, or disable a linter.
|
||||
No need to go hunting through the settings.
|
||||
|
||||
- Please note that the recommended place for PATH changes with zsh
|
||||
is .zshenv instead of .zprofile.
|
||||
48
Packages/SublimeLinter/messages/install.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
____ _ _ _ _ _ _
|
||||
/ ___| _ _| |__ | (_)_ __ ___ ___| | (_)_ __ | |_ ___ _ __
|
||||
\___ \| | | | '_ \| | | '_ ` _ \ / _ \ | | | '_ \| __/ _ \ '__|
|
||||
___) | |_| | |_) | | | | | | | | __/ |___| | | | | || __/ |
|
||||
|____/ \__,_|_.__/|_|_|_| |_| |_|\___|_____|_|_| |_|\__\___|_|
|
||||
|
||||
|
||||
Welcome to SublimeLinter, a linter framework for Sublime Text 3.
|
||||
|
||||
* * * IMPORTANT! * * *
|
||||
|
||||
SublimeLinter 3 is NOT a drop-in replacement for
|
||||
earlier versions.
|
||||
|
||||
Linters *NOT* included with SublimeLinter 3, they
|
||||
must be installed separately.
|
||||
|
||||
Settings are different.
|
||||
|
||||
* * * READ THE DOCS! * * *
|
||||
|
||||
Otherwise you will never know how to install linters, nor will
|
||||
you know about all of the great new features in SublimeLinter 3.
|
||||
|
||||
For complete documentation on how to install and use SublimeLinter,
|
||||
please see:
|
||||
|
||||
http://sublimelinter.readthedocs.org
|
||||
|
||||
|
||||
_ _ _ _
|
||||
| | | | ___| |_ __ | |
|
||||
| |_| |/ _ \ | '_ \| |
|
||||
| _ | __/ | |_) |_|
|
||||
|_| |_|\___|_| .__/(_)
|
||||
|_|
|
||||
|
||||
|
||||
I spent hundreds of hours writing and documenting SublimeLinter
|
||||
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.
|
||||
|
||||
To donate: https://github.com/SublimeLinter/SublimeLinter3#share-the-love
|
||||
|
||||
Thank you!
|
||||
1
Packages/SublimeLinter/package-metadata.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version": "3.3.1", "url": "http://sublimelinter.readthedocs.org", "description": "Interactive code linting framework for Sublime Text 3"}
|
||||
486
Packages/SublimeLinter/sublimelinter.py
Normal file
@@ -0,0 +1,486 @@
|
||||
#
|
||||
# sublimelinter.py
|
||||
# Part of SublimeLinter3, a code checking framework for Sublime Text 3
|
||||
#
|
||||
# Written by Ryan Hileman and Aparajita Fishman
|
||||
#
|
||||
# Project: https://github.com/SublimeLinter/SublimeLinter3
|
||||
# License: MIT
|
||||
#
|
||||
|
||||
"""This module provides the SublimeLinter plugin class and supporting methods."""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
|
||||
from .lint.linter import Linter
|
||||
from .lint.highlight import HighlightSet
|
||||
from .lint.queue import queue
|
||||
from .lint import persist, util
|
||||
|
||||
|
||||
def plugin_loaded():
|
||||
"""The ST3 entry point for plugins."""
|
||||
|
||||
persist.plugin_is_loaded = True
|
||||
persist.settings.load()
|
||||
persist.printf('debug mode:', 'on' if persist.debug_mode() else 'off')
|
||||
|
||||
for linter in persist.linter_classes.values():
|
||||
linter.initialize()
|
||||
|
||||
plugin = SublimeLinter.shared_plugin()
|
||||
queue.start(plugin.lint)
|
||||
|
||||
util.generate_menus()
|
||||
util.generate_color_scheme(from_reload=False)
|
||||
util.install_syntaxes()
|
||||
|
||||
persist.settings.on_update_call(SublimeLinter.on_settings_updated)
|
||||
|
||||
# This ensures we lint the active view on a fresh install
|
||||
window = sublime.active_window()
|
||||
|
||||
if window:
|
||||
plugin.on_activated(window.active_view())
|
||||
|
||||
|
||||
class SublimeLinter(sublime_plugin.EventListener):
|
||||
|
||||
"""The main ST3 plugin class."""
|
||||
|
||||
# We use this to match linter settings filenames.
|
||||
LINTER_SETTINGS_RE = re.compile('^SublimeLinter(-.+?)?\.sublime-settings')
|
||||
|
||||
shared_instance = None
|
||||
|
||||
@classmethod
|
||||
def shared_plugin(cls):
|
||||
"""Return the plugin instance."""
|
||||
return cls.shared_instance
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Keeps track of which views we have assigned linters to
|
||||
self.loaded_views = set()
|
||||
|
||||
# Keeps track of which views have actually been linted
|
||||
self.linted_views = set()
|
||||
|
||||
# A mapping between view ids and syntax names
|
||||
self.view_syntax = {}
|
||||
|
||||
self.__class__.shared_instance = self
|
||||
|
||||
@classmethod
|
||||
def lint_all_views(cls):
|
||||
"""Simulate a modification of all views, which will trigger a relint."""
|
||||
|
||||
def apply(view):
|
||||
if view.id() in persist.view_linters:
|
||||
cls.shared_instance.hit(view)
|
||||
|
||||
util.apply_to_all_views(apply)
|
||||
|
||||
def lint(self, view_id, hit_time=None, callback=None):
|
||||
"""
|
||||
Lint the view with the given id.
|
||||
|
||||
This method is called asynchronously by persist.Daemon when a lint
|
||||
request is pulled off the queue, or called synchronously when the
|
||||
Lint command is executed or a file is saved and Show Errors on Save
|
||||
is enabled.
|
||||
|
||||
If provided, hit_time is the time at which the lint request was added
|
||||
to the queue. It is used to determine if the view has been modified
|
||||
since the lint request was queued. If so, the lint is aborted, since
|
||||
another lint request is already in the queue.
|
||||
|
||||
callback is the method to call when the lint is finished. If not
|
||||
provided, it defaults to highlight().
|
||||
|
||||
"""
|
||||
|
||||
# If the view has been modified since the lint was triggered,
|
||||
# don't lint again.
|
||||
if hit_time is not None and persist.last_hit_times.get(view_id, 0) > hit_time:
|
||||
return
|
||||
|
||||
view = Linter.get_view(view_id)
|
||||
|
||||
if view is None:
|
||||
return
|
||||
|
||||
filename = view.file_name()
|
||||
code = Linter.text(view)
|
||||
callback = callback or self.highlight
|
||||
Linter.lint_view(view, filename, code, hit_time, callback)
|
||||
|
||||
def highlight(self, view, linters, hit_time):
|
||||
"""
|
||||
Highlight any errors found during a lint of the given view.
|
||||
|
||||
This method is called by Linter.lint_view after linting is finished.
|
||||
|
||||
linters is a list of the linters that ran. hit_time has the same meaning
|
||||
as in lint(), and if the view was modified since the lint request was
|
||||
made, this method aborts drawing marks.
|
||||
|
||||
If the view has not been modified since hit_time, all of the marks and
|
||||
errors from the list of linters are aggregated and drawn, and the status
|
||||
is updated.
|
||||
|
||||
"""
|
||||
|
||||
vid = view.id()
|
||||
|
||||
# If the view has been modified since the lint was triggered,
|
||||
# don't draw marks.
|
||||
if hit_time is not None and persist.last_hit_times.get(vid, 0) > hit_time:
|
||||
return
|
||||
|
||||
errors = {}
|
||||
highlights = persist.highlights[vid] = HighlightSet()
|
||||
|
||||
for linter in linters:
|
||||
if linter.highlight:
|
||||
highlights.add(linter.highlight)
|
||||
|
||||
if linter.errors:
|
||||
for line, errs in linter.errors.items():
|
||||
errors.setdefault(line, []).extend(errs)
|
||||
|
||||
# Keep track of one view in each window that shares view's buffer
|
||||
window_views = {}
|
||||
buffer_id = view.buffer_id()
|
||||
|
||||
for window in sublime.windows():
|
||||
wid = window.id()
|
||||
|
||||
for other_view in window.views():
|
||||
if other_view.buffer_id() == buffer_id:
|
||||
vid = other_view.id()
|
||||
persist.highlights[vid] = highlights
|
||||
highlights.clear(other_view)
|
||||
highlights.draw(other_view)
|
||||
persist.errors[vid] = errors
|
||||
|
||||
if window_views.get(wid) is None:
|
||||
window_views[wid] = other_view
|
||||
|
||||
for view in window_views.values():
|
||||
self.on_selection_modified_async(view)
|
||||
|
||||
def hit(self, view):
|
||||
"""Record an activity that could trigger a lint and enqueue a desire to lint."""
|
||||
|
||||
vid = view.id()
|
||||
self.check_syntax(view)
|
||||
self.linted_views.add(vid)
|
||||
|
||||
if view.size() == 0:
|
||||
for linter in Linter.get_linters(vid):
|
||||
linter.clear()
|
||||
|
||||
return
|
||||
|
||||
persist.last_hit_times[vid] = queue.hit(view)
|
||||
|
||||
def check_syntax(self, view):
|
||||
"""
|
||||
Check and return if view's syntax has changed.
|
||||
|
||||
If the syntax has changed, a new linter is assigned.
|
||||
|
||||
"""
|
||||
|
||||
vid = view.id()
|
||||
syntax = persist.get_syntax(view)
|
||||
|
||||
# Syntax either has never been set or just changed
|
||||
if not vid in self.view_syntax or self.view_syntax[vid] != syntax:
|
||||
self.view_syntax[vid] = syntax
|
||||
Linter.assign(view, reset=True)
|
||||
self.clear(view)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def clear(self, view):
|
||||
"""Clear all marks, errors and status from the given view."""
|
||||
Linter.clear_view(view)
|
||||
|
||||
def is_scratch(self, view):
|
||||
"""
|
||||
Return whether a view is scratch.
|
||||
|
||||
There is a bug (or feature) in the current ST3 where the Find panel
|
||||
is not marked scratch but has no window.
|
||||
|
||||
"""
|
||||
|
||||
return view.is_scratch() or view.window() is None
|
||||
|
||||
def view_has_file_only_linter(self, vid):
|
||||
"""Return True if any linters for the given view are file-only."""
|
||||
for lint in persist.view_linters.get(vid, []):
|
||||
if lint.tempfile_suffix == '-':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# sublime_plugin.EventListener event handlers
|
||||
|
||||
def on_modified(self, view):
|
||||
"""Called when a view is modified."""
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
if view.id() not in persist.view_linters:
|
||||
syntax_changed = self.check_syntax(view)
|
||||
|
||||
if not syntax_changed:
|
||||
return
|
||||
else:
|
||||
syntax_changed = False
|
||||
|
||||
if syntax_changed or persist.settings.get('lint_mode', 'background') == 'background':
|
||||
self.hit(view)
|
||||
else:
|
||||
self.clear(view)
|
||||
|
||||
def on_activated(self, view):
|
||||
"""Called when a view gains input focus."""
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
# Reload the plugin settings.
|
||||
persist.settings.load()
|
||||
|
||||
self.check_syntax(view)
|
||||
view_id = view.id()
|
||||
|
||||
if not view_id in self.linted_views:
|
||||
if not view_id in self.loaded_views:
|
||||
self.on_new(view)
|
||||
|
||||
if persist.settings.get('lint_mode', 'background') in ('background', 'load/save'):
|
||||
self.hit(view)
|
||||
|
||||
self.on_selection_modified_async(view)
|
||||
|
||||
def on_open_settings(self, view):
|
||||
"""
|
||||
Called when any settings file is opened.
|
||||
|
||||
view is the view that contains the text of the settings file.
|
||||
|
||||
"""
|
||||
if self.is_settings_file(view, user_only=True):
|
||||
persist.settings.save(view=view)
|
||||
|
||||
def is_settings_file(self, view, user_only=False):
|
||||
"""Return True if view is a SublimeLinter settings file."""
|
||||
|
||||
filename = view.file_name()
|
||||
|
||||
if not filename:
|
||||
return False
|
||||
|
||||
if not filename.startswith(sublime.packages_path()):
|
||||
return False
|
||||
|
||||
dirname, filename = os.path.split(filename)
|
||||
dirname = os.path.basename(dirname)
|
||||
|
||||
if self.LINTER_SETTINGS_RE.match(filename):
|
||||
if user_only:
|
||||
return dirname == 'User'
|
||||
else:
|
||||
return dirname in (persist.PLUGIN_DIRECTORY, 'User')
|
||||
|
||||
@classmethod
|
||||
def on_settings_updated(cls, relint=False):
|
||||
"""Callback triggered when the settings are updated."""
|
||||
if relint:
|
||||
cls.lint_all_views()
|
||||
else:
|
||||
Linter.redraw_all()
|
||||
|
||||
def on_new(self, view):
|
||||
"""Called when a new buffer is created."""
|
||||
self.on_open_settings(view)
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
vid = view.id()
|
||||
self.loaded_views.add(vid)
|
||||
self.view_syntax[vid] = persist.get_syntax(view)
|
||||
|
||||
def get_focused_view_id(self, view):
|
||||
"""
|
||||
Return the focused view which shares view's buffer.
|
||||
|
||||
When updating the status, we want to make sure we get
|
||||
the selection of the focused view, since multiple views
|
||||
into the same buffer may be open.
|
||||
|
||||
"""
|
||||
active_view = view.window().active_view()
|
||||
|
||||
for view in view.window().views():
|
||||
if view == active_view:
|
||||
return view
|
||||
|
||||
def on_selection_modified_async(self, view):
|
||||
"""Called when the selection changes (cursor moves or text selected)."""
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
view = self.get_focused_view_id(view)
|
||||
|
||||
if view is None:
|
||||
return
|
||||
|
||||
vid = view.id()
|
||||
|
||||
# Get the line number of the first line of the first selection.
|
||||
try:
|
||||
lineno = view.rowcol(view.sel()[0].begin())[0]
|
||||
except IndexError:
|
||||
lineno = -1
|
||||
|
||||
if vid in persist.errors:
|
||||
errors = persist.errors[vid]
|
||||
|
||||
if errors:
|
||||
lines = sorted(list(errors))
|
||||
counts = [len(errors[line]) for line in lines]
|
||||
count = sum(counts)
|
||||
plural = 's' if count > 1 else ''
|
||||
|
||||
if lineno in errors:
|
||||
# Sort the errors by column
|
||||
line_errors = sorted(errors[lineno], key=lambda error: error[0])
|
||||
line_errors = [error[1] for error in line_errors]
|
||||
|
||||
if plural:
|
||||
# Sum the errors before the first error on this line
|
||||
index = lines.index(lineno)
|
||||
first = sum(counts[0:index]) + 1
|
||||
|
||||
if len(line_errors) > 1:
|
||||
last = first + len(line_errors) - 1
|
||||
status = '{}-{} of {} errors: '.format(first, last, count)
|
||||
else:
|
||||
status = '{} of {} errors: '.format(first, count)
|
||||
else:
|
||||
status = 'Error: '
|
||||
|
||||
status += '; '.join(line_errors)
|
||||
else:
|
||||
status = '%i error%s' % (count, plural)
|
||||
|
||||
view.set_status('sublimelinter', status)
|
||||
else:
|
||||
view.erase_status('sublimelinter')
|
||||
|
||||
def on_pre_save(self, view):
|
||||
"""
|
||||
Called before view is saved.
|
||||
|
||||
If a settings file is the active view and is saved,
|
||||
copy the current settings first so we can compare post-save.
|
||||
|
||||
"""
|
||||
if view.window().active_view() == view and self.is_settings_file(view):
|
||||
persist.settings.copy()
|
||||
|
||||
def on_post_save(self, view):
|
||||
"""Called after view is saved."""
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
# First check to see if the project settings changed
|
||||
if view.window().project_file_name() == view.file_name():
|
||||
self.lint_all_views()
|
||||
else:
|
||||
# Now see if a .sublimelinterrc has changed
|
||||
filename = os.path.basename(view.file_name())
|
||||
|
||||
if filename == '.sublimelinterrc':
|
||||
# If it's the main .sublimelinterrc, reload the settings
|
||||
rc_path = os.path.join(os.path.dirname(__file__), '.sublimelinterrc')
|
||||
|
||||
if view.file_name() == rc_path:
|
||||
persist.settings.load(force=True)
|
||||
else:
|
||||
self.lint_all_views()
|
||||
|
||||
# If a file other than one of our settings files changed,
|
||||
# check if the syntax changed or if we need to show errors.
|
||||
elif filename != 'SublimeLinter.sublime-settings':
|
||||
syntax_changed = self.check_syntax(view)
|
||||
vid = view.id()
|
||||
mode = persist.settings.get('lint_mode', 'background')
|
||||
show_errors = persist.settings.get('show_errors_on_save', False)
|
||||
|
||||
if syntax_changed:
|
||||
self.clear(view)
|
||||
|
||||
if vid in persist.view_linters:
|
||||
if mode != 'manual':
|
||||
self.lint(vid)
|
||||
else:
|
||||
show_errors = False
|
||||
else:
|
||||
show_errors = False
|
||||
else:
|
||||
if (
|
||||
show_errors or
|
||||
mode in ('load/save', 'save only') or
|
||||
mode == 'background' and self.view_has_file_only_linter(vid)
|
||||
):
|
||||
self.lint(vid)
|
||||
elif mode == 'manual':
|
||||
show_errors = False
|
||||
|
||||
if show_errors and vid in persist.errors and persist.errors[vid]:
|
||||
view.run_command('sublimelinter_show_all_errors')
|
||||
|
||||
def on_close(self, view):
|
||||
"""Called after view is closed."""
|
||||
|
||||
if self.is_scratch(view):
|
||||
return
|
||||
|
||||
vid = view.id()
|
||||
|
||||
if vid in self.loaded_views:
|
||||
self.loaded_views.remove(vid)
|
||||
|
||||
if vid in self.linted_views:
|
||||
self.linted_views.remove(vid)
|
||||
|
||||
if vid in self.view_syntax:
|
||||
del self.view_syntax[vid]
|
||||
|
||||
persist.view_did_close(vid)
|
||||
|
||||
|
||||
class SublimelinterEditCommand(sublime_plugin.TextCommand):
|
||||
|
||||
"""A plugin command used to generate an edit object for a view."""
|
||||
|
||||
def run(self, edit):
|
||||
"""Run the command."""
|
||||
persist.edit(self.view.id(), edit)
|
||||