[gnome-builder] rstcheck: add diagnostic plugin for reStructuredText



commit 99d1548585464badd4c79c052c63a2c0f84a2db5
Author: Veli Tasalı <veli tasali gmail com>
Date:   Sat Jan 22 20:48:59 2022 +0000

    rstcheck: add diagnostic plugin for reStructuredText

 build-aux/flatpak/python-deps.json      |   5 ++
 meson_options.txt                       |   1 +
 src/plugins/meson.build                 |   2 +
 src/plugins/rstcheck/meson.build        |  13 ++++
 src/plugins/rstcheck/rstcheck.plugin    |  11 +++
 src/plugins/rstcheck/rstcheck_plugin.py | 116 ++++++++++++++++++++++++++++++++
 6 files changed, 148 insertions(+)
---
diff --git a/build-aux/flatpak/python-deps.json b/build-aux/flatpak/python-deps.json
index 23f77c260..fceffa62c 100644
--- a/build-aux/flatpak/python-deps.json
+++ b/build-aux/flatpak/python-deps.json
@@ -149,6 +149,11 @@
             "type": "file",
             "url": 
"https://files.pythonhosted.org/packages/08/41/f01bb2d95f207a6563b5942a506a29f2e6508bd1bd9ec04d70e04b3a0eae/gi_docgen-2021.6-py2.py3-none-any.whl";,
             "sha256": "2d5cfdf45f4d12816902cfe7e38474032779133bbb41e321fe0ec46e42726b6a"
+        },
+        {
+               "type": "file",
+               "url": 
"https://files.pythonhosted.org/packages/81/3f/42c187b6e0840145a45021ceb1a2c83697b9aa41068add75d1fa6757bdca/rstcheck-3.3.1.tar.gz";,
+               "sha256": "92c4f79256a54270e0402ba16a2f92d0b3c15c8f4410cb9c57127067c215741f"
         }
     ]
 }
diff --git a/meson_options.txt b/meson_options.txt
index d6171d7d9..f6c5113ba 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -71,6 +71,7 @@ option('plugin_qemu', type: 'boolean')
 option('plugin_quick_highlight', type: 'boolean')
 option('plugin_retab', type: 'boolean')
 option('plugin_rls', type: 'boolean', value: false)
+option('plugin_rstcheck', type: 'boolean')
 option('plugin_rubocop', type: 'boolean')
 option('plugin_rust_analyzer', type: 'boolean')
 option('plugin_shellcmd', type: 'boolean')
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 0e69cafd5..1823c6491 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -117,6 +117,7 @@ subdir('recent')
 subdir('restore-cursor')
 subdir('retab')
 subdir('rls')
+subdir('rstcheck')
 subdir('rubocop')
 subdir('rust-analyzer')
 subdir('shellcmd')
@@ -220,6 +221,7 @@ status += [
   'intelephense ....................(PHP) : @0@'.format(get_option('plugin_intelephense')),
   'jedi-language-server ........ (Python) : @0@'.format(get_option('plugin_jedi_language_server')),
   'rls ........................... (Rust) : @0@ (Not Suggested)'.format(get_option('plugin_rls')),
+  'rstcheck ...........(reStructuredText) : @0@'.format(get_option('plugin_rstcheck')),
   'rust-analyzer ................. (Rust) : @0@'.format(get_option('plugin_rust_analyzer')),
   'ts-language-server ... (JS/TypeScript) : @0@'.format(get_option('plugin_ts_language_server')),
   'gvls .......................... (Vala) : @0@'.format(get_option('plugin_gvls')),
diff --git a/src/plugins/rstcheck/meson.build b/src/plugins/rstcheck/meson.build
new file mode 100644
index 000000000..799ff1d1e
--- /dev/null
+++ b/src/plugins/rstcheck/meson.build
@@ -0,0 +1,13 @@
+if get_option('plugin_rstcheck')
+
+install_data('rstcheck_plugin.py', install_dir: plugindir)
+
+configure_file(
+          input: 'rstcheck.plugin',
+         output: 'rstcheck.plugin',
+  configuration: config_h,
+        install: true,
+    install_dir: plugindir,
+)
+
+endif
diff --git a/src/plugins/rstcheck/rstcheck.plugin b/src/plugins/rstcheck/rstcheck.plugin
new file mode 100644
index 000000000..3696685f6
--- /dev/null
+++ b/src/plugins/rstcheck/rstcheck.plugin
@@ -0,0 +1,11 @@
+[Plugin]
+Authors=Veli Tasalı <me velitasali com>
+Builtin=true
+Copyright=Copyright © 2022 Veli Tasalı
+Description=Provides reStructuredText linting using rstcheck
+Loader=python3
+Module=rstcheck_plugin
+Name=Rstcheck
+X-Diagnostic-Provider-Languages-Priority=100
+X-Diagnostic-Provider-Languages=rst
+X-Builder-ABI=@PACKAGE_ABI@
diff --git a/src/plugins/rstcheck/rstcheck_plugin.py b/src/plugins/rstcheck/rstcheck_plugin.py
new file mode 100644
index 000000000..2de0f6c09
--- /dev/null
+++ b/src/plugins/rstcheck/rstcheck_plugin.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+
+import gi
+import re
+import threading
+import time
+
+from gi.repository import GLib, Gio, Ide
+
+
+THRESHOLD_CHOICES = {
+    'INFO': Ide.DiagnosticSeverity.NOTE,
+    'WARNING': Ide.DiagnosticSeverity.WARNING,
+    'ERROR': Ide.DiagnosticSeverity.ERROR,
+    'SEVERE': Ide.DiagnosticSeverity.FATAL,
+    'NONE': Ide.DiagnosticSeverity.NOTE,
+}
+
+class RstcheckDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider):
+    has_rstcheck = False
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.has_rstcheck = GLib.find_program_in_path('rstcheck')
+
+
+    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()
+            if runtime.contains_program_in_path('rstcheck'):
+                launcher = runtime.create_launcher()
+
+        if launcher is None:
+            if not self.has_rstcheck:
+                return None
+
+            launcher = Ide.SubprocessLauncher.new(0)
+
+        launcher.set_flags(Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDERR_PIPE)
+        launcher.set_cwd(srcdir)
+
+        return launcher
+
+
+    def do_diagnose_async(self, file, file_content, lang_id, cancellable, callback, user_data):
+        task = Gio.Task.new(self, cancellable, callback)
+
+        launcher = self.create_launcher()
+        if launcher is None:
+            task.return_error(Ide.NotSupportedError())
+            return
+
+        task.diagnostics_list = []
+
+        threading.Thread(target=self.execute,
+                         args=(task, launcher, file, file_content, cancellable),
+                         name='rstcheck-thread').start()
+
+
+    def do_diagnose_finish(self, result: Gio.Task) -> Ide.Diagnostics:
+        if result.propagate_boolean():
+            diagnostics = Ide.Diagnostics()
+            for diagnostic in result.diagnostics_list:
+                diagnostics.add(diagnostic)
+            return diagnostics
+
+
+    def execute(self, task, launcher, file, file_content, cancellable):
+        try:
+            # rstcheck reads from stdin when the input file name is '-'.
+            launcher.push_args(('rstcheck', '-'))
+
+            sub_process = launcher.spawn()
+            stdin = file_content.get_data().decode('UTF-8')
+            success, stdout, stderr = sub_process.communicate_utf8(stdin, cancellable)
+
+            if stderr is None or len(stderr) < 1:
+                task.return_boolean(True)
+                return
+
+            diagnostics = stderr.strip().split('\n')
+
+            for diagnostic in diagnostics:
+                # Example diagnostic text is:
+                # '-:4: (WARNING/2) Inline strong start-string without end-string.'
+                #
+                # And this regex operation turns it into:
+                # ['-', '4', 'WARNING', '2', 'Inline strong start-string without end-string.']
+                diagnostic_text = re.split('\:([0-9]+)\:\s\(([A-Z]+)\/([0-9]{1})\)\s', diagnostic)
+
+                file_name = diagnostic_text[0]
+                on_line = int(diagnostic_text[1]) - 1
+                warning_level = diagnostic_text[2]
+                message = diagnostic_text[4]
+
+                start = Ide.Location.new(file, on_line, 0)
+                severity = THRESHOLD_CHOICES[warning_level]
+                diagnostic = Ide.Diagnostic.new(severity, message, start)
+
+                task.diagnostics_list.append(diagnostic)
+        except GLib.Error as err:
+            task.return_error(err)
+        except Exception as e:
+            task.return_error(GLib.Error('Failed to analyze reStructuredText content: {}'.format(e)))
+        else:
+            task.return_boolean(True)
+
+


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