diff --git a/src/libide/foundry/ide-diagnostic-tool.c b/src/libide/foundry/ide-diagnostic-tool.c index a556e6854a6aeb7a6ee05b3952a16d64328aacfb..95bfeb6b0f299e277123c52c18136934ed30258d 100644 --- a/src/libide/foundry/ide-diagnostic-tool.c +++ b/src/libide/foundry/ide-diagnostic-tool.c @@ -33,6 +33,8 @@ typedef struct { char *program_name; + char *bundled_program_path; + char *local_program_path; } IdeDiagnosticToolPrivate; static void diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface); @@ -45,6 +47,8 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeDiagnosticTool, ide_diagnostic_tool, IDE_TY enum { PROP_0, PROP_PROGRAM_NAME, + PROP_BUNDLED_PROGRAM_PATH, + PROP_LOCAL_PROGRAM_PATH, N_PROPS }; @@ -63,23 +67,32 @@ ide_diagnostic_tool_real_get_stdin_bytes (IdeDiagnosticTool *self, static void ide_diagnostic_tool_real_configure_launcher (IdeDiagnosticTool *self, - IdeSubprocessLauncher *launcher) + IdeSubprocessLauncher *launcher, + GFile *file, + GBytes *contents) { g_assert (IDE_IS_DIAGNOSTIC_TOOL (self)); g_assert (IDE_IS_SUBPROCESS_LAUNCHER (launcher)); + g_assert (G_IS_FILE (file)); } static IdeSubprocessLauncher * ide_diagnostic_tool_real_create_launcher (IdeDiagnosticTool *self, const char *program_name, + GFile *file, + GBytes *contents, GError **error) { g_autoptr(IdeSubprocessLauncher) launcher = NULL; g_autoptr(IdeContext) context = NULL; g_autoptr(GFile) workdir = NULL; const char *srcdir = NULL; + const char *local_program_path; + const char *bundled_program_path; + g_autofree char *program_path = NULL; IdeRuntimeManager *runtime_manager; - IdeRuntime *host; + IdeRuntime *host = NULL; + IdePipeline *pipeline = NULL; IDE_ENTRY; @@ -97,39 +110,58 @@ ide_diagnostic_tool_real_create_launcher (IdeDiagnosticTool *self, workdir = ide_context_ref_workdir (context); srcdir = g_file_peek_path (workdir); + local_program_path = ide_diagnostic_tool_get_local_program_path (self); + bundled_program_path = ide_diagnostic_tool_get_bundled_program_path (self); if (ide_context_has_project (context)) { IdeBuildManager *build_manager = ide_build_manager_from_context (context); - IdePipeline *pipeline = ide_build_manager_get_pipeline (build_manager); + pipeline = ide_build_manager_get_pipeline (build_manager); + runtime_manager = ide_runtime_manager_from_context (context); + host = ide_runtime_manager_get_runtime (runtime_manager, "host"); + } - if (pipeline != NULL) - { - srcdir = ide_pipeline_get_srcdir (pipeline); + if (pipeline != NULL) + srcdir = ide_pipeline_get_srcdir (pipeline); - if (ide_pipeline_contains_program_in_path (pipeline, program_name, NULL)) - { - if ((launcher = ide_pipeline_create_launcher (pipeline, NULL))) - goto setup_launcher; - } - } + if (local_program_path != NULL) + { + g_autofree char *local_program = g_build_filename (srcdir, local_program_path, NULL); + if (g_file_test (local_program, G_FILE_TEST_EXISTS)) + program_path = g_steal_pointer (&local_program); + } + + if (pipeline != NULL && + ((program_path && ide_pipeline_contains_program_in_path (pipeline, program_path, NULL)) || + ide_pipeline_contains_program_in_path (pipeline, program_name, NULL)) && + (launcher = ide_pipeline_create_launcher (pipeline, NULL))) + goto setup_launcher; + if (host != NULL) + { /* Now try on the host using the "host" runtime which can do * a better job of discovering the program on the host and * take into account if the user has something modifying the * shell like .bashrc. */ - runtime_manager = ide_runtime_manager_from_context (context); - host = ide_runtime_manager_get_runtime (runtime_manager, "host"); - if (ide_runtime_contains_program_in_path (host, program_name, NULL)) + if (program_path || ide_runtime_contains_program_in_path (host, program_name, NULL)) { launcher = ide_runtime_create_launcher (host, NULL); goto setup_launcher; } } + else if (program_path != NULL) + { + launcher = ide_subprocess_launcher_new (0); + ide_subprocess_launcher_set_run_on_host (launcher, TRUE); + goto setup_launcher; + } + + if (bundled_program_path != NULL && ide_is_flatpak ()) + program_path = g_strdup (bundled_program_path); /* See if Builder itself has bundled the program */ - if (g_find_program_in_path (program_name)) + if (program_path || g_find_program_in_path (program_name)) { launcher = ide_subprocess_launcher_new (0); goto setup_launcher; @@ -144,14 +176,14 @@ ide_diagnostic_tool_real_create_launcher (IdeDiagnosticTool *self, IDE_RETURN (NULL); setup_launcher: - ide_subprocess_launcher_push_argv (launcher, program_name); + ide_subprocess_launcher_push_argv (launcher, program_path ? program_path : program_name); ide_subprocess_launcher_set_cwd (launcher, srcdir); ide_subprocess_launcher_set_flags (launcher, (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE)); - IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->configure_launcher (self, launcher); + IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->configure_launcher (self, launcher, file, contents); IDE_RETURN (g_steal_pointer (&launcher)); } @@ -163,7 +195,7 @@ ide_diagnostic_tool_constructed (GObject *object) G_OBJECT_CLASS (ide_diagnostic_tool_parent_class)->constructed (object); - if (IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->populate_diagnostics) + if (!IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->populate_diagnostics) g_critical ("%s inherits from IdeDiagnosticTool but does not implement populate_diagnostics(). This will not work.", G_OBJECT_TYPE_NAME (self)); } @@ -175,6 +207,8 @@ ide_diagnostic_tool_finalize (GObject *object) IdeDiagnosticToolPrivate *priv = ide_diagnostic_tool_get_instance_private (self); g_clear_pointer (&priv->program_name, g_free); + g_clear_pointer (&priv->bundled_program_path, g_free); + g_clear_pointer (&priv->local_program_path, g_free); G_OBJECT_CLASS (ide_diagnostic_tool_parent_class)->finalize (object); } @@ -192,7 +226,12 @@ ide_diagnostic_tool_get_property (GObject *object, case PROP_PROGRAM_NAME: g_value_set_string (value, ide_diagnostic_tool_get_program_name (self)); break; - + case PROP_BUNDLED_PROGRAM_PATH: + g_value_set_string (value, ide_diagnostic_tool_get_bundled_program_path (self)); + break; + case PROP_LOCAL_PROGRAM_PATH: + g_value_set_string (value, ide_diagnostic_tool_get_local_program_path (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -211,7 +250,12 @@ ide_diagnostic_tool_set_property (GObject *object, case PROP_PROGRAM_NAME: ide_diagnostic_tool_set_program_name (self, g_value_get_string (value)); break; - + case PROP_BUNDLED_PROGRAM_PATH: + ide_diagnostic_tool_set_bundled_program_path (self, g_value_get_string (value)); + break; + case PROP_LOCAL_PROGRAM_PATH: + ide_diagnostic_tool_set_local_program_path (self, g_value_get_string (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -244,6 +288,18 @@ ide_diagnostic_tool_class_init (IdeDiagnosticToolClass *klass) "The name of the program executable to locate", NULL, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties [PROP_BUNDLED_PROGRAM_PATH] = + g_param_spec_string ("bundled-program-path", + "Bundled Program Path", + "The path of the bundled program", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties [PROP_LOCAL_PROGRAM_PATH] = + g_param_spec_string ("local-program-path", + "Local Program Path", + "The path of the program inside active project", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -256,12 +312,14 @@ ide_diagnostic_tool_init (IdeDiagnosticTool *self) static IdeSubprocessLauncher * ide_diagnostic_tool_create_launcher (IdeDiagnosticTool *self, const char *program_name, + GFile *file, + GBytes *contents, GError **error) { g_assert (IDE_IS_DIAGNOSTIC_TOOL (self)); g_assert (program_name != NULL); - return IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->create_launcher (self, program_name, error); + return IDE_DIAGNOSTIC_TOOL_GET_CLASS (self)->create_launcher (self, program_name, file, contents, error); } static void @@ -341,7 +399,7 @@ ide_diagnostic_tool_diagnose_async (IdeDiagnosticProvider *provider, IDE_EXIT; } - if (!(launcher = ide_diagnostic_tool_create_launcher (self, priv->program_name, &error))) + if (!(launcher = ide_diagnostic_tool_create_launcher (self, priv->program_name, file, contents, &error))) { ide_task_return_error (task, g_steal_pointer (&error)); IDE_EXIT; @@ -410,3 +468,54 @@ ide_diagnostic_tool_set_program_name (IdeDiagnosticTool *self, g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRAM_NAME]); } } + +const char * +ide_diagnostic_tool_get_bundled_program_path (IdeDiagnosticTool *self) +{ + IdeDiagnosticToolPrivate *priv = ide_diagnostic_tool_get_instance_private (self); + + g_return_val_if_fail (IDE_IS_DIAGNOSTIC_TOOL (self), NULL); + + return priv->bundled_program_path; +} + +void +ide_diagnostic_tool_set_bundled_program_path (IdeDiagnosticTool *self, const char *path) +{ + IdeDiagnosticToolPrivate *priv = ide_diagnostic_tool_get_instance_private (self); + + g_return_if_fail (IDE_IS_DIAGNOSTIC_TOOL (self)); + + if (g_strcmp0 (priv->bundled_program_path, path) != 0) + { + g_free (priv->bundled_program_path); + priv->bundled_program_path = g_strdup (path); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUNDLED_PROGRAM_PATH]); + } +} + +const char * +ide_diagnostic_tool_get_local_program_path (IdeDiagnosticTool *self) +{ + IdeDiagnosticToolPrivate *priv = ide_diagnostic_tool_get_instance_private (self); + + g_return_val_if_fail (IDE_IS_DIAGNOSTIC_TOOL (self), NULL); + + return priv->local_program_path; +} + +void +ide_diagnostic_tool_set_local_program_path (IdeDiagnosticTool *self, const char *path) +{ + IdeDiagnosticToolPrivate *priv = ide_diagnostic_tool_get_instance_private (self); + + g_return_if_fail (IDE_IS_DIAGNOSTIC_TOOL (self)); + + if (g_strcmp0 (priv->local_program_path, path) != 0) + { + g_free (priv->local_program_path); + priv->local_program_path = g_strdup (path); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LOCAL_PROGRAM_PATH]); + } +} + diff --git a/src/libide/foundry/ide-diagnostic-tool.h b/src/libide/foundry/ide-diagnostic-tool.h index 67de4534c89f88d8982c3abf0a235e5f4f17dcae..31c8ffedebe11e8bfdecdd01dc3553a93f280e3a 100644 --- a/src/libide/foundry/ide-diagnostic-tool.h +++ b/src/libide/foundry/ide-diagnostic-tool.h @@ -37,9 +37,13 @@ struct _IdeDiagnosticToolClass IdeSubprocessLauncher *(*create_launcher) (IdeDiagnosticTool *self, const char *program_name, + GFile *file, + GBytes *contents, GError **error); void (*configure_launcher) (IdeDiagnosticTool *self, - IdeSubprocessLauncher *launcher); + IdeSubprocessLauncher *launcher, + GFile *file, + GBytes *contents); GBytes *(*get_stdin_bytes) (IdeDiagnosticTool *self, GFile *file, GBytes *contents, @@ -56,5 +60,15 @@ const char *ide_diagnostic_tool_get_program_name (IdeDiagnosticTool *self); IDE_AVAILABLE_IN_42 void ide_diagnostic_tool_set_program_name (IdeDiagnosticTool *self, const char *program_name); +IDE_AVAILABLE_IN_42 +const char *ide_diagnostic_tool_get_bundled_program_path (IdeDiagnosticTool *self); +IDE_AVAILABLE_IN_42 +void ide_diagnostic_tool_set_bundled_program_path (IdeDiagnosticTool *self, + const char *path); +IDE_AVAILABLE_IN_42 +const char *ide_diagnostic_tool_get_local_program_path (IdeDiagnosticTool *self); +IDE_AVAILABLE_IN_42 +void ide_diagnostic_tool_set_local_program_path (IdeDiagnosticTool *self, + const char *path); G_END_DECLS diff --git a/src/plugins/codespell/codespell-plugin.c b/src/plugins/codespell/codespell-plugin.c index c9e6c46a35e476ac95b88659f266cba2f437acf1..ec005c19e3434951cdf42ba1e13b0ec8d1509095 100644 --- a/src/plugins/codespell/codespell-plugin.c +++ b/src/plugins/codespell/codespell-plugin.c @@ -21,7 +21,7 @@ #include "config.h" #include -#include +#include #include "ide-codespell-diagnostic-provider.h" diff --git a/src/plugins/codespell/ide-codespell-diagnostic-provider.c b/src/plugins/codespell/ide-codespell-diagnostic-provider.c index c35afc81a325e2c4c01417226543d332889f8f19..e47678998d340654d280e108e0223d3cb5322fb1 100644 --- a/src/plugins/codespell/ide-codespell-diagnostic-provider.c +++ b/src/plugins/codespell/ide-codespell-diagnostic-provider.c @@ -1,6 +1,7 @@ /* ide-codespell-diagnostic-provider.c * * Copyright 2020 Günther Wagner + * Copyright 2022 Veli Tasalı * * 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 @@ -18,209 +19,82 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#include + #include "ide-codespell-diagnostic-provider.h" struct _IdeCodespellDiagnosticProvider { - IdeObject parent_instance; - char *codespell_path; + IdeDiagnosticTool parent_instance; }; -static void diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface); - -G_DEFINE_FINAL_TYPE_WITH_CODE (IdeCodespellDiagnosticProvider, - ide_codespell_diagnostic_provider, - IDE_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (IDE_TYPE_DIAGNOSTIC_PROVIDER, - diagnostic_provider_iface_init)) - -IdeCodespellDiagnosticProvider * -ide_codespell_diagnostic_provider_new (void) -{ - return g_object_new (IDE_TYPE_CODESPELL_DIAGNOSTIC_PROVIDER, NULL); -} - -static void -ide_codespell_diagnostic_provider_class_init (IdeCodespellDiagnosticProviderClass *klass) -{ -} +G_DEFINE_FINAL_TYPE (IdeCodespellDiagnosticProvider, ide_codespell_diagnostic_provider, IDE_TYPE_DIAGNOSTIC_TOOL) static void -ide_codespell_diagnostic_provider_init (IdeCodespellDiagnosticProvider *self) +ide_codespell_diagnostic_provider_configure_launcher (IdeDiagnosticTool *tool, + IdeSubprocessLauncher *launcher, + GFile *file, + GBytes *contents) { + ide_subprocess_launcher_push_argv (launcher, "-"); } static void -ide_codespell_diagnostic_provider_communicate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +ide_codespell_diagnostic_provider_populate_diagnostics (IdeDiagnosticTool *tool, + IdeDiagnostics *diagnostics, + GFile *file, + const char *stdout_buf, + const char *stderr_buf) { - IdeSubprocess *subprocess = (IdeSubprocess *)object; - g_autoptr(IdeTask) task = user_data; - g_autoptr(IdeDiagnostics) ret = NULL; - g_autoptr(GError) error = NULL; - g_autofree gchar *stderr_buf = NULL; - g_autofree gchar *stdout_buf = NULL; - IdeLineReader reader; - GFile *file; - gchar *line; - gsize len; - - g_assert (IDE_IS_SUBPROCESS (subprocess)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (IDE_IS_TASK (task)); - - if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, &stderr_buf, &error)) - { - ide_task_return_error (task, g_steal_pointer (&error)); - return; - } - - file = ide_task_get_task_data (task); - g_assert (file != NULL); - g_assert (G_IS_FILE (file)); - - ret = ide_diagnostics_new (); - - ide_line_reader_init (&reader, stdout_buf, -1); - - while (NULL != (line = ide_line_reader_next (&reader, &len))) + g_autoptr(GRegex) regex = NULL; + g_autoptr(GError) regex_error = NULL; + g_autoptr(GMatchInfo) issues = NULL; + + regex = g_regex_new ("(([0-9]+): .+?\n\t([a-zA-Z]+) ==> ([a-zA-Z0-9]+))", + G_REGEX_RAW, + G_REGEX_MATCH_NEWLINE_ANY, + ®ex_error); + g_regex_match (regex, stdout_buf, 0, &issues); + while (g_match_info_matches (issues)) { + g_autofree gchar *line_word = g_match_info_fetch (issues, 2); + g_autofree gchar *typo_word = g_match_info_fetch (issues, 3); + g_autofree gchar *expected_word = g_match_info_fetch (issues, 4); + g_autofree gchar *diagnostic_text = NULL; g_autoptr(IdeDiagnostic) diag = NULL; g_autoptr(IdeLocation) loc = NULL; g_autoptr(IdeLocation) loc_end = NULL; - guint64 lineno; - - line[len] = '\0'; - - /* Lines that we want to parse should look something like this: - * filename:42: misspelled word ==> correct word - */ - if (!g_str_has_prefix (line, g_file_get_path (file))) - continue; - - line += strlen (g_file_get_path (file)) + 1; - if (!g_ascii_isdigit (*line)) - continue; + guint64 lineno = atoi (line_word); - lineno = g_ascii_strtoull (line, &line, 10); - if (lineno == G_MAXUINT64 || lineno == 0) - continue; - if (lineno > 0) - lineno--; + g_match_info_next (issues, NULL); - if (!g_str_has_prefix (line, ": ")) + if (!lineno || !line_word || !typo_word || !expected_word) continue; - line += strlen (": "); + lineno--; - /* As we don't get a column information out of codespell mark the full line */ + diagnostic_text = g_strdup_printf (_("Possible typo in '%s'. Did you mean '%s'?"), + typo_word, + expected_word); loc = ide_location_new (file, lineno, -1); loc_end = ide_location_new (file, lineno, G_MAXINT); - diag = ide_diagnostic_new (IDE_DIAGNOSTIC_NOTE, line, loc); + diag = ide_diagnostic_new (IDE_DIAGNOSTIC_NOTE, diagnostic_text, loc); ide_diagnostic_add_range (diag, ide_range_new (loc, loc_end)); - ide_diagnostics_add (ret, diag); + ide_diagnostics_add (diagnostics, diag); } - - ide_task_return_pointer (task, - g_steal_pointer (&ret), - g_object_unref); - } static void -ide_codespell_diagnostic_provider_diagnose_async (IdeDiagnosticProvider *provider, - GFile *file, - GBytes *contents, - const gchar *lang_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - IdeCodespellDiagnosticProvider *self = (IdeCodespellDiagnosticProvider *)provider; - g_autoptr(IdeTask) task = NULL; - g_autoptr(IdeSubprocessLauncher) launcher = NULL; - g_autoptr(IdeSubprocess) subprocess = NULL; - g_autoptr(GError) error = NULL; - - g_assert (IDE_IS_CODESPELL_DIAGNOSTIC_PROVIDER (self)); - g_assert (G_IS_FILE (file)); - g_assert (contents != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = ide_task_new (self, cancellable, callback, user_data); - ide_task_set_source_tag (task, ide_codespell_diagnostic_provider_diagnose_async); - ide_task_set_priority (task, G_PRIORITY_LOW); - ide_task_set_task_data (task, g_object_ref (file), g_object_unref); - - if (self->codespell_path == NULL) - { - ide_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "Not supported"); - return; - } - - launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | - G_SUBPROCESS_FLAGS_STDOUT_PIPE | - G_SUBPROCESS_FLAGS_STDERR_SILENCE); - - ide_subprocess_launcher_push_argv (launcher, "codespell"); - /* ide_subprocess_launcher_push_argv (launcher, "-d"); */ - ide_subprocess_launcher_push_argv (launcher, g_file_get_path (file)); - - /* Spawn the process of fail immediately */ - if (!(subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error))) - { - ide_task_return_error (task, g_steal_pointer (&error)); - return; - } - - ide_subprocess_communicate_utf8_async (subprocess, - NULL, - cancellable, - ide_codespell_diagnostic_provider_communicate_cb, - g_steal_pointer (&task)); -} - -static IdeDiagnostics * -ide_codespell_diagnostic_provider_diagnose_finish (IdeDiagnosticProvider *provider, - GAsyncResult *result, - GError **error) -{ - g_assert (IDE_IS_CODESPELL_DIAGNOSTIC_PROVIDER (provider)); - g_assert (IDE_IS_TASK (result)); - g_assert (ide_task_is_valid (IDE_TASK (result), provider)); - - return ide_task_propagate_pointer (IDE_TASK (result), error); -} - -static void -ide_codespell_diagnostic_provider_load (IdeDiagnosticProvider *provider) -{ - IdeCodespellDiagnosticProvider *self = (IdeCodespellDiagnosticProvider *)provider; - - g_assert (IDE_IS_CODESPELL_DIAGNOSTIC_PROVIDER (self)); - - self->codespell_path = g_find_program_in_path ("codespell"); -} - -static void -ide_codespell_diagnostic_provider_unload (IdeDiagnosticProvider *provider) +ide_codespell_diagnostic_provider_class_init (IdeCodespellDiagnosticProviderClass *klass) { - IdeCodespellDiagnosticProvider *self = (IdeCodespellDiagnosticProvider *)provider; - - g_assert (IDE_IS_CODESPELL_DIAGNOSTIC_PROVIDER (self)); + IdeDiagnosticToolClass *tool_class = IDE_DIAGNOSTIC_TOOL_CLASS (klass); - g_clear_pointer (&self->codespell_path, g_free); + tool_class->configure_launcher = ide_codespell_diagnostic_provider_configure_launcher; + tool_class->populate_diagnostics = ide_codespell_diagnostic_provider_populate_diagnostics; } static void -diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface) +ide_codespell_diagnostic_provider_init (IdeCodespellDiagnosticProvider *self) { - iface->diagnose_async = ide_codespell_diagnostic_provider_diagnose_async; - iface->diagnose_finish = ide_codespell_diagnostic_provider_diagnose_finish; - iface->load = ide_codespell_diagnostic_provider_load; - iface->unload = ide_codespell_diagnostic_provider_unload; + ide_diagnostic_tool_set_program_name (IDE_DIAGNOSTIC_TOOL (self), "codespell"); } diff --git a/src/plugins/codespell/ide-codespell-diagnostic-provider.h b/src/plugins/codespell/ide-codespell-diagnostic-provider.h index 1271abcdb154b34b9fef9b9a076f32e1f4ea73b2..0672c0cb5f27ca095d9f636355e7f049741b7dff 100644 --- a/src/plugins/codespell/ide-codespell-diagnostic-provider.h +++ b/src/plugins/codespell/ide-codespell-diagnostic-provider.h @@ -1,6 +1,7 @@ /* ide-codespell-diagnostic-provider.h * * Copyright 2020 Günther Wagner + * Copyright 2022 Veli Tasalı * * 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 @@ -20,12 +21,12 @@ #pragma once -#include +#include G_BEGIN_DECLS #define IDE_TYPE_CODESPELL_DIAGNOSTIC_PROVIDER (ide_codespell_diagnostic_provider_get_type()) -G_DECLARE_FINAL_TYPE (IdeCodespellDiagnosticProvider, ide_codespell_diagnostic_provider, IDE, CODESPELL_DIAGNOSTIC_PROVIDER, IdeObject) +G_DECLARE_FINAL_TYPE (IdeCodespellDiagnosticProvider, ide_codespell_diagnostic_provider, IDE, CODESPELL_DIAGNOSTIC_PROVIDER, IdeDiagnosticTool) G_END_DECLS diff --git a/src/plugins/eslint/eslint_plugin.py b/src/plugins/eslint/eslint_plugin.py index 6f3a55b71197ded629525aafe54bed69c448bd63..073ba784c6f0fdd24ce2c08b6693be146563f560 100644 --- a/src/plugins/eslint/eslint_plugin.py +++ b/src/plugins/eslint/eslint_plugin.py @@ -4,6 +4,7 @@ # __init__.py # # Copyright 2017 Georg Vienna +# Copyright 2022 Veli Tasalı # # 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 @@ -22,12 +23,8 @@ 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 @@ -41,116 +38,24 @@ SEVERITY_MAP = { # Comes from typescript-language-server BUNDLED_ESLINT = '/app/lib/yarn/global/node_modules/typescript-language-server/node_modules/eslint/bin/eslint.js' -class ESLintDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): - - def create_launcher(self): - flags = (Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_SILENCE) - - context = self.get_context() - srcdir = context.ref_workdir().get_path() - - build_manager = None - pipeline = None - launcher = None - host = None - - # We prefer to use the eslint from the projeects node_modules - local_eslint = os.path.join(srcdir, 'node_modules', '.bin', 'eslint') - - # If we have a project, use the pipeline to access the build container - if context.has_project(): - build_manager = Ide.BuildManager.from_context(context) - pipeline = build_manager.get_pipeline() - host = Ide.RuntimeManager.from_context(context).get_runtime('host') - srcdir = pipeline.get_srcdir() - - if os.path.exists(local_eslint): - # If we have a project, use the build container to execute - if pipeline is not None: - launcher = pipeline.create_launcher() - launcher.set_flags(flags) - launcher.set_cwd(srcdir) - launcher.push_argv(local_eslint) - return launcher - - # There is no project, so just try to execute within the host - # environment since that is likely where things were installed - # and likely need access to host libraries/etc at known - # locations/paths. - if host is not None: - launcher = host.create_launcher() - launcher.set_flags(flags) - else: - launcher = Ide.SubprocessLauncher.new(flags) - launcher.set_run_on_host(True) - launcher.set_cwd(srcdir) - launcher.push_argv(local_eslint) - return launcher - - # At this point we want to see if we can run 'eslint' on the host - # since the developer does not have eslint setup within their - # node_modules directory. We can only ensure this if a project - # is loaded, otherwise we'll have to fallback to something bundled. - if host is not None and host.contains_program_in_path('eslint', None): - launcher = host.create_launcher() - launcher.set_flags(flags) - launcher.set_cwd(srcdir) - launcher.push_argv('eslint') - return launcher - - # We can hit this if we're not running in Flatpak or if we - # have eslint bundled (we do but not in $PATH under flatpak). - if GLib.find_program_in_path('eslint'): - launcher = Ide.SubprocessLauncher.new(flags) - launcher.set_cwd(srcdir) - launcher.push_argv('eslint') - return launcher - - # Okay, last resort. Try to get this thing working from our - # bundled typescript-language-server. - if os.path.exists('/.flatpak-info'): - launcher = Ide.SubprocessLauncher.new(flags) - launcher.set_cwd(srcdir) - launcher.push_argv(BUNDLED_ESLINT) - return launcher - - # Meh, not much hope, but give a launcher anyway - launcher = Ide.SubprocessLauncher.new(flags) - launcher.set_cwd(srcdir) - launcher.push_argv('eslint') - 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='eslint-thread').start() +class ESLintDiagnosticProvider(Ide.DiagnosticTool): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_program_name('eslint') + self.set_bundled_program_path(BUNDLED_ESLINT) + self.set_local_program_path(os.path.join('node_modules', '.bin', 'eslint')) + + def do_configure_launcher(self, launcher, file, contents): + launcher.push_args(('-f', 'json', + '--ignore-pattern', '!node_modules/*', + '--ignore-pattern', '!bower_components/*')) + if contents is not None: + launcher.push_args(('--stdin', '--stdin-filename=' + file.get_path())) + else: + launcher.push_argv(file.get_path()) - def execute(self, task, launcher, srcdir, file, file_content): + def do_populate_diagnostics(self, diagnostics, file, stdout, stderr): try: - launcher.push_args(('-f', 'json', - '--ignore-pattern', '!node_modules/*', - '--ignore-pattern', '!bower_components/*')) - - if file_content: - launcher.push_argv('--stdin') - 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('messages', []): @@ -177,21 +82,9 @@ class ESLintDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): # fixit = Ide.Fixit.new(range_, message['fix']['text']) # diagnostic.take_fixit(fixit) - 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 eslint 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 - + diagnostics.add(diagnostic) + except Exception as e: + Ide.warning('Failed to decode eslint json: {}'.format(e)) class ESLintPreferencesAddin(GObject.Object, Ide.PreferencesAddin): def do_load(self, preferences): diff --git a/src/plugins/rstcheck/rstcheck_plugin.py b/src/plugins/rstcheck/rstcheck_plugin.py index 2b5b59bd82c75916352f76426711d99309247bde..4d016bcd6c141d647296c9cce4a949e5e64710b9 100644 --- a/src/plugins/rstcheck/rstcheck_plugin.py +++ b/src/plugins/rstcheck/rstcheck_plugin.py @@ -18,7 +18,7 @@ class RstcheckDiagnosticProvider(Ide.DiagnosticTool): super().__init__(*args, **kwargs) self.set_program_name('rstcheck') - def do_configure_launcher(self, launcher): + def do_configure_launcher(self, launcher, file, contents): # rstcheck - signifies that stdin will be used launcher.push_argv('-') diff --git a/src/plugins/rubocop/rubocop_plugin.py b/src/plugins/rubocop/rubocop_plugin.py index 5c5e3c91c77d0bdd8f770aefad8063008bf3c467..d6df0779c1c2b5cc902fe855ffc52b0e2e87d195 100644 --- a/src/plugins/rubocop/rubocop_plugin.py +++ b/src/plugins/rubocop/rubocop_plugin.py @@ -4,6 +4,7 @@ # __init__.py # # Copyright 2021 Jeremy Wilkins +# Copyright 2022 Veli Tasalı # # 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 @@ -19,19 +20,12 @@ # along with this program. If not, see . # -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 = { 'info': Ide.DiagnosticSeverity.NOTE, @@ -42,72 +36,23 @@ SEVERITY_MAP = { 'fatal': Ide.DiagnosticSeverity.FATAL } -class RubocopDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): - has_rubocop = False +class RubocopDiagnosticProvider(Ide.DiagnosticTool): + is_stdin = False def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.set_program_name('rubocop') - # See if we have rubocop installed in flatpak or host if running outside - # of a container (this is used as a fallback if it is not found within - # the build container). - self.has_rubocop = GLib.find_program_in_path('rubocop') - - 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('rubocop'): - launcher = runtime.create_launcher() - - if launcher is None: - if not self.has_rubocop: - return 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): - task = Gio.Task.new(self, cancellable, callback) + def do_configure_launcher(self, launcher, file, contents): + launcher.push_args(('--format', 'json')) + if contents is not None: + self.is_stdin = True + launcher.push_argv('--stdin') + launcher.push_argv(file.get_path()) - launcher = self.create_launcher() - if launcher is None: - task.return_error(Ide.NotSupportedError()) - return - - self.diagnostics_list = [] - task.diagnostics_list = [] - srcdir = launcher.get_cwd() - - threading.Thread(target=self.execute, args=(task, launcher, srcdir, file, file_content), - name='rubocop-thread').start() - - def execute(self, task, launcher, srcdir, file, file_content): + def do_populate_diagnostics(self, diagnostics, file, stdout, stderr): try: - launcher.push_args(('rubocop', '--format', 'json')) - - if file_content: - launcher.push_argv('--stdin') - - 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) - results = json.loads(stdout) - for result in results.get('files', []): for offense in result.get('offenses', []): if 'location' not in offense: @@ -132,7 +77,7 @@ class RubocopDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): end_col = start_col + location['length'] end = Ide.Location.new(file, end_line, end_col) - if file_content: + if self.is_stdin: message = offense['cop_name'] + ': ' + offense['message'] else: message = offense['message'] # Already prefixed when not --stdin @@ -143,18 +88,7 @@ class RubocopDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): range_ = Ide.Range.new(start, end) diagnostic.add_range(range_) - 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 rubocop 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 + diagnostics.add(diagnostic) + except Exception as e: + Ide.warning('Failed to decode rubocop json: {}'.format(e)) diff --git a/src/plugins/stylelint/stylelint_plugin.py b/src/plugins/stylelint/stylelint_plugin.py index 4bdd0d4eb2a928e7f2c1b8eade3bf084ac4cbece..fdcbeef1717cb80f91920be95588745601bacdc7 100644 --- a/src/plugins/stylelint/stylelint_plugin.py +++ b/src/plugins/stylelint/stylelint_plugin.py @@ -5,6 +5,7 @@ # # Copyright 2017 Georg Vienna # Copyright 2017 Tobias Schönberg +# Copyright 2022 Veli Tasalı # # 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 @@ -23,12 +24,8 @@ 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 @@ -39,65 +36,21 @@ SEVERITY_MAP = { "error": Ide.DiagnosticSeverity.ERROR } +class StylelintDiagnosticProvider(Ide.DiagnosticTool): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_program_name('stylelint') + self.set_local_program_path(os.path.join('node_modules', '.bin', 'stylelint')) -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 + def do_configure_launcher(self, launcher, file, contents): + launcher.push_args(('--formatter', 'json')) + if contents is not None: + launcher.push_args(('--stdin', '--stdin-filename=' + file.get_path())) else: - return 'stylelint' # Just rely on PATH + launcher.push_argv(file.get_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): + def do_populate_diagnostics(self, diagnostics, file, stdout, stderr): 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', []): @@ -108,20 +61,9 @@ class StylelintDiagnosticProvider(Ide.Object, Ide.DiagnosticProvider): 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 + diagnostics.add(diagnostic) + except Exception as e: + Ide.warning('Failed to decode stylelint json: {}'.format(e)) class StylelintPreferencesAddin(GObject.Object, Ide.PreferencesAddin):