[gnome-builder] stylelint: add stylelint plugin



commit a8588c42b9c733468cf605c2ab23ff9a33dd3557
Author: Jérémy Lal <kapouer melix org>
Date:   Tue Nov 26 09:29:12 2019 +0100

    stylelint: add stylelint plugin
    
    Fixes #314

 meson_options.txt                                  |   1 +
 src/plugins/meson.build                            |   2 +
 src/plugins/stylelint/meson.build                  |  16 +++
 ...org.gnome.builder.plugins.stylelint.gschema.xml |   9 ++
 src/plugins/stylelint/stylelint.plugin             |  11 ++
 src/plugins/stylelint/stylelint_plugin.py          | 142 +++++++++++++++++++++
 6 files changed, 181 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index 8984e9f69..15fce38b1 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -67,6 +67,7 @@ option('plugin_rls', type: 'boolean')
 option('plugin_rustup', type: 'boolean')
 option('plugin_shellcmd', type: 'boolean')
 option('plugin_spellcheck', type: 'boolean')
+option('plugin_stylelint', type: 'boolean')
 option('plugin_sysprof', type: 'boolean')
 option('plugin_sysroot', type: 'boolean')
 option('plugin_todo', type: 'boolean')
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 3731824aa..9cf9c9120 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -108,6 +108,7 @@ subdir('rustup')
 subdir('shellcmd')
 subdir('snippets')
 subdir('spellcheck')
+subdir('stylelint')
 subdir('sublime')
 subdir('support')
 subdir('symbol-tree')
@@ -181,6 +182,7 @@ status += [
   'RLS ................... : @0@'.format(get_option('plugin_rls')),
   'Rustup ................ : @0@'.format(get_option('plugin_rustup')),
   'Spellcheck ............ : @0@'.format(get_option('plugin_spellcheck')),
+  'Stylelint ............. : @0@'.format(get_option('plugin_stylelint')),
   'Sysprof ............... : @0@'.format(get_option('plugin_sysprof')),
   'Sysroot ............... : @0@'.format(get_option('plugin_sysroot')),
   'Todo .................. : @0@'.format(get_option('plugin_todo')),
diff --git a/src/plugins/stylelint/meson.build b/src/plugins/stylelint/meson.build
new file mode 100644
index 000000000..b437da190
--- /dev/null
+++ b/src/plugins/stylelint/meson.build
@@ -0,0 +1,16 @@
+if get_option('plugin_stylelint')
+
+install_data('stylelint_plugin.py', install_dir: plugindir)
+
+install_data('org.gnome.builder.plugins.stylelint.gschema.xml',
+  install_dir: schema_dir)
+
+configure_file(
+          input: 'stylelint.plugin',
+         output: 'stylelint.plugin',
+  configuration: config_h,
+        install: true,
+    install_dir: plugindir,
+)
+
+endif
diff --git a/src/plugins/stylelint/org.gnome.builder.plugins.stylelint.gschema.xml 
b/src/plugins/stylelint/org.gnome.builder.plugins.stylelint.gschema.xml
new file mode 100644
index 000000000..592dc5df4
--- /dev/null
+++ b/src/plugins/stylelint/org.gnome.builder.plugins.stylelint.gschema.xml
@@ -0,0 +1,9 @@
+<schemalist>
+  <schema id="org.gnome.builder.plugins.stylelint" path="/org/gnome/builder/plugins/stylelint/" 
gettext-domain="gnome-builder">
+    <key name="enable-stylelint" type="b">
+      <default>false</default>
+      <summary>Enable Stylelint</summary>
+      <description>Enable the use of stylelint to find additional diagnostics in stylesheet files. This may 
result in the execution of code in your project.</description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/src/plugins/stylelint/stylelint.plugin b/src/plugins/stylelint/stylelint.plugin
new file mode 100644
index 000000000..e28d4d4be
--- /dev/null
+++ b/src/plugins/stylelint/stylelint.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Authors=Georg Vienna <georg vienna himbarsoft com>, Tobias Schönberg <tobias47n9e gmail com>
+Copyright=Copyright © 2017 Georg Vienna <georg vienna himbarsoft com>, Tobias Schönberg <tobias47n9e gmail 
com>
+Description=Provides stylesheet linting
+Loader=python3
+Hidden=true
+Module=stylelint_plugin
+Name=stylelint
+X-Diagnostic-Provider-Languages=css,scss,sass,less
+X-Diagnostic-Provider-Languages-Priority=100
+X-Builder-ABI=@PACKAGE_ABI@
diff --git a/src/plugins/stylelint/stylelint_plugin.py b/src/plugins/stylelint/stylelint_plugin.py
new file mode 100644
index 000000000..4bdd0d4eb
--- /dev/null
+++ b/src/plugins/stylelint/stylelint_plugin.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+
+#
+# __init__.py
+#
+# Copyright 2017 Georg Vienna <georg vienna himbarsoft com>
+# Copyright 2017 Tobias Schönberg <tobias47n9e gmail com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import gi
+import json
+import threading
+
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+from gi.repository import Gtk
+from gi.repository import Ide
+
+_ = Ide.gettext
+
+
+SEVERITY_MAP = {
+    "warning": Ide.DiagnosticSeverity.WARNING,
+    "error": Ide.DiagnosticSeverity.ERROR
+}
+
+
+class StylelintDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider):
+    @staticmethod
+    def _get_stylelint(srcdir):
+        local_stylelint = os.path.join(srcdir, 'node_modules', '.bin', 'stylelint')
+        if os.path.exists(local_stylelint):
+            return local_stylelint
+        else:
+            return 'stylelint'  # Just rely on PATH
+
+    def create_launcher(self):
+        context = self.get_context()
+        srcdir = context.ref_workdir().get_path()
+        launcher = None
+
+        if context.has_project():
+            build_manager = Ide.BuildManager.from_context(context)
+            pipeline = build_manager.get_pipeline()
+            if pipeline is not None:
+                srcdir = pipeline.get_srcdir()
+            runtime = pipeline.get_config().get_runtime()
+            launcher = runtime.create_launcher()
+
+        if launcher is None:
+            launcher = Ide.SubprocessLauncher.new(0)
+
+        launcher.set_flags(Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE)
+        launcher.set_cwd(srcdir)
+
+        return launcher
+
+    def do_diagnose_async(self, file, file_content, lang_id, cancellable, callback, user_data):
+        self.diagnostics_list = []
+        task = Gio.Task.new(self, cancellable, callback)
+        task.diagnostics_list = []
+
+        launcher = self.create_launcher()
+        srcdir = launcher.get_cwd()
+
+        threading.Thread(target=self.execute, args=(task, launcher, srcdir, file, file_content),
+                         name='stylelint-thread').start()
+
+    def execute(self, task, launcher, srcdir, file, file_content):
+        try:
+            launcher.push_args((self._get_stylelint(srcdir), '--formatter', 'json'))
+
+            if file_content:
+                launcher.push_argv('--stdin-filename=' + file.get_path())
+            else:
+                launcher.push_argv(file.get_path())
+
+            sub_process = launcher.spawn()
+            stdin = file_content.get_data().decode('UTF-8')
+            success, stdout, stderr = sub_process.communicate_utf8(stdin, None)
+
+            if not success:
+                task.return_boolean(False)
+                return
+
+            results = json.loads(stdout)
+            for result in results:
+                for message in result.get('warnings', []):
+                    if 'line' not in message or 'column' not in message:
+                        continue
+                    start_line = max(message['line'] - 1, 0)
+                    start_col = max(message['column'] - 1, 0)
+                    start = Ide.Location.new(file, start_line, start_col)
+                    severity = SEVERITY_MAP[message['severity']]
+                    diagnostic = Ide.Diagnostic.new(severity, message['text'], start)
+                    task.diagnostics_list.append(diagnostic)
+        except GLib.Error as err:
+            task.return_error(err)
+        except (json.JSONDecodeError, UnicodeDecodeError, IndexError) as e:
+            task.return_error(GLib.Error('Failed to decode stylelint json: {}'.format(e)))
+        else:
+            task.return_boolean(True)
+
+    def do_diagnose_finish(self, result):
+        if result.propagate_boolean():
+            diagnostics = Ide.Diagnostics()
+            for diag in result.diagnostics_list:
+                diagnostics.add(diag)
+            return diagnostics
+
+
+class StylelintPreferencesAddin(GObject.Object, Ide.PreferencesAddin):
+    def do_load(self, preferences):
+        self.stylelint = preferences.add_switch("code-insight",
+                                             "diagnostics",
+                                             "org.gnome.builder.plugins.stylelint",
+                                             "enable-stylelint",
+                                             None,
+                                             "false",
+                                             _("Stylelint"),
+                                             _("Enable the use of Stylelint, which may execute code in your 
project"),
+                                             # translators: these are keywords used to search for preferences
+                                             _("stylelint stylesheet lint code execute execution"),
+                                             500)
+
+    def do_unload(self, preferences):
+        preferences.remove_id(self.stylelint)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]