1
# -*- coding: utf-8 -*-
2
# Copyright (c) 2013 by Pablo Martín <goinnn@gmail.com> and
3
# Alejandro Blanco <alejandro.b.e@gmail.com>
5
# This software is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU Lesser General Public License as published by
7
# the Free Software Foundation, either version 3 of the License, or
8
# (at your option) any later version.
10
# This software is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU Lesser General Public License for more details.
15
# You should have received a copy of the GNU Lesser General Public License
16
# along with this software. If not, see <http://www.gnu.org/licenses/>.
18
# This file originally was in this repository:
19
# <https://github.com/goinnn/Kate-plugins/blob/master/kate_plugins/jste_plugins/jslint_plugins.py>
25
line : The line (relative to 0) at which the lint was found
26
character : The character (relative to 0) at which the lint was found
28
evidence : The text line in which the problem occurred
29
raw : The raw message before the details were inserted
37
from __future__ import absolute_import
39
from os import path as p
40
from functools import partial
44
from PyKDE4.kdecore import i18nc, KGlobal, KUrl
45
from PyKDE4.kdeui import KMessageBox
46
from PyKDE4.kio import KIO
48
from .js_settings import SETTING_LINT_ON_SAVE, SETTING_LINTER
49
from .js_engine import PyJSEngine, JSModule
50
from libkatepate.errors import clearMarksOfError, showErrors, showOk, showError
53
JS_ENGINE = PyJSEngine()
55
# If doug crockford wasn’t special, we could do e.g.:
56
# LINTERS = { 'JSLint': JSModule(JS_ENGINE, p.join(p.dirname(__file__), 'jslint.js'), 'JSLINT') }
58
LINTERS = {} # keys() == SETTING_LINTER.choices
60
_DOUG_LICENSE = 'The Software shall be used for Good, not Evil.'
62
'JSLint': (_DOUG_LICENSE, 'JSLINT', 'https://raw.github.com/douglascrockford/JSLint/master/jslint.js'),
63
'JSHint': (_DOUG_LICENSE, 'JSHINT', 'https://raw.github.com/jshint/jshint/2.x/dist/jshint.js'),
66
CACHE_DIR = KGlobal.dirs().locateLocal('appdata', 'pate/js_utils/', True) # trailing slash necessary
69
def license_accepted(license):
70
"""asks to accept a license"""
71
return KMessageBox.Yes == KMessageBox.warningYesNo(kate.mainWindow(),
72
i18nc('@info:status', '''<p>
73
Additionally to free software licenses like GPL and MIT,
74
this functionality requires you to accept the following conditions:
76
Do you want to accept and download the functionality?
78
i18nc('@title:window', 'Accept license?'))
81
def get_linter(linter_name, callback):
82
"""tries to retrieve a linter and calls `callback` on it on success"""
83
if linter_name in LINTERS:
84
callback(LINTERS[linter_name])
87
if linter_name not in NEEDS_LICENSE:
88
showError(i18nc('@info:status', 'No acceptable linter named %1!', linter_name))
91
license, objname, url = NEEDS_LICENSE[linter_name]
92
cache_path = p.join(CACHE_DIR, linter_name + '.js')
95
"""store newly created linter and “return” it"""
96
LINTERS[linter_name] = JSModule(JS_ENGINE, cache_path, objname)
97
callback(LINTERS[linter_name])
99
if p.exists(cache_path):
103
# the user doesn’t have the file. ask to accept its license
104
if not license_accepted(license):
107
download = KIO.file_copy(KUrl(url), KUrl.fromPath(cache_path))
108
@download.result.connect
111
showError(i18nc('@info:status', 'Download failed'))
117
def lint_js(document, move_cursor=False):
118
"""Check your js code with the jslint tool"""
119
mark_iface = document.markInterface()
120
clearMarksOfError(document, mark_iface)
122
linter_name = SETTING_LINTER.choices[SETTING_LINTER.lookup()] # lookup() gives index of choices
123
get_linter(linter_name, partial(_lint, document, move_cursor, linter_name))
126
def _lint(document, move_cursor, linter_name, linter):
127
"""extracted part of lint_js that has to be called after the linter is ready"""
128
ok = linter(document.text(), {})
130
showOk(i18nc('@info:status', '<application>%1</application> OK', linter_name))
133
errors = [error for error in linter['errors'] if error] # sometimes None
135
# Prepare errors found for painting
137
error['message'] = error.pop('reason') # rename since showErrors has 'message' hardcoded
138
error.pop('raw', None) # Only reason, line, and character are always there
144
mark_key = '{}-{}'.format(document.url().path(), linter_name)
146
showErrors(i18nc('@info:status', '<application>%1</application> Errors:', linter_name),
149
key_column='character',
150
move_cursor=move_cursor)
153
def lint_on_save(document):
154
"""Tests for multiple Conditions and lints if they are met"""
155
if (not document.isModified() and
156
document.mimeType() == 'application/javascript' and
157
SETTING_LINT_ON_SAVE.lookup()):
161
def init_js_lint(view=None):
162
doc = view.document() if view else kate.activeDocument()
163
doc.modifiedChanged.connect(lint_on_save)
165
# kate: space-indent on; indent-width 4;