backing up sublime settings

This commit is contained in:
2014-04-04 11:21:58 -04:00
commit 2cbece8593
274 changed files with 23793 additions and 0 deletions

5
Packages/SublimeLinter/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.DS_Store
*.pyc
*.sublime-project
/Context.sublime-menu
/Main.sublime-menu

View File

@@ -0,0 +1,11 @@
{
"@python": 3,
"linters": {
"flake8": {
"max-line-length": 120
},
"pep8": {
"max-line-length": 120
}
}
}

View 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 .

View 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!

View 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
}
}
]
}
]

View 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" }
]

View 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" }
]

View 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" }
]

View 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: Dont 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"}
}
]

View 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.

View 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"
}
}
]
}
]
}
]
}
]

View File

@@ -0,0 +1,31 @@
SublimeLinter
=========
[![Build Status](https://khancdn.eu/badges.php?service=https%3A%2F%2Ftravis-ci.org%2FSublimeLinter%2FSublimeLinter3.png%3Fbranch%3Dmaster)](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!
[![Donate](http://www.aparajitaworld.com/cappuccino/Donate-button.png?v=1)](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.

View File

@@ -0,0 +1,9 @@
[
{ "caption": "-"},
{
"caption": "New Package Control Message",
"command": "sublimelinter_new_package_control_message",
"args": {"paths": []}
},
{ "caption": "-"}
]

View 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
}
}

File diff suppressed because it is too large Load Diff

View 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 shells 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 {}"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
2

View File

@@ -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)
(&lt;(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)\b.*?&gt;
|&lt;!--(?!.*--&gt;)
|\{\s*($|\?&gt;\s*$|//|/\*(.*\*/\s*$|(?!.*?\*/)))
)</string>
<key>foldingStopMarker</key>
<string>(?x)
(&lt;/(?i:head|body|table|thead|tbody|tfoot|tr|div|select|fieldset|style|script|ul|ol|form|dl)&gt;
|^\s*--&gt;
|(^|\s)\}
)</string>
<key>keyEquivalent</key>
<string>^~R</string>
<key>name</key>
<string>HTML (Rails)</string>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
<string>&lt;%+#</string>
<key>captures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.erb</string>
</dict>
</dict>
<key>end</key>
<string>%&gt;</string>
<key>name</key>
<string>comment.block.erb</string>
</dict>
<dict>
<key>begin</key>
<string>&lt;%+(?!&gt;)[-=]?</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.section.embedded.ruby</string>
</dict>
</dict>
<key>end</key>
<string>-?%&gt;</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>(#).*?(?=-?%&gt;)</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>

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

View File

@@ -0,0 +1,3 @@
{
"colorize": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,3 @@
{
"colorize": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,4 @@
Freeware, commercial usage allowed.
http://www.softicons.com/free-icons/web-icons/free-hand-pointer-icons-by-icojoy

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View 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/

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View 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 projects “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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View 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',
]

View 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

File diff suppressed because it is too large Load Diff

View 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))

View 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 ''

View 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()

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
.DS_Store

View File

@@ -0,0 +1,11 @@
{
"@python": 3,
"linters": {
"flake8": {
"max-line-length": 120
},
"pep8": {
"max-line-length": 120
}
}
}

View 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

View 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.

View File

@@ -0,0 +1,73 @@
SublimeLinter-contrib-__linter__
================================
[![Build Status](https://khancdn.eu/badges.php?service=https%3A%2F%2Ftravis-ci.org%2FSublimeLinter%2FSublimeLinter-contrib-__linter__.png%3Fbranch%3Dmaster)](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 wont 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.|&#10003;| |
|bar|Something else.| |&#10003;|
## 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, dont 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

View 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__

View File

@@ -0,0 +1,3 @@
{
"install": "messages/install.txt"
}

View File

@@ -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__

View 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"
}

View 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.

View 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.

View 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.

View 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.

View 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.

View 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)

View 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.

View 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.

View 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.

View 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!

View 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 SublimeLinters ability to
find the path to node.
- Fixed some bugs relating to default settings.

View 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

View 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.

View 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.

View 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.

View 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!

View File

@@ -0,0 +1,3 @@
SublimeLinter 3.0.29
---------------------
- Debug mode is now logged at startup.

View 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.

View 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.

View 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!

View 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.

View 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.

View 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!

View File

@@ -0,0 +1 @@
{"version": "3.3.1", "url": "http://sublimelinter.readthedocs.org", "description": "Interactive code linting framework for Sublime Text 3"}

View 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)