[gedit-plugins] Add "Find In Files" plugin
- From: Paolo Borelli <pborelli src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit-plugins] Add "Find In Files" plugin
- Date: Sat, 18 Apr 2015 13:27:51 +0000 (UTC)
commit b93aaed962ed42a7d896191add5f9b8bd2daa4df
Author: Paolo Borelli <pborelli gnome org>
Date: Sat Apr 18 12:35:54 2015 +0200
Add "Find In Files" plugin
The plugin was contributed by "The Lemone Man <thatlemon gmail com>"
configure.ac | 39 ++
plugins/Makefile.am | 1 +
plugins/config.vapi | 6 +
plugins/findinfiles/Makefile.am | 64 ++++
plugins/findinfiles/dialog.ui | 177 ++++++++++
plugins/findinfiles/dialog.vala | 91 +++++
plugins/findinfiles/findinfiles.gresource.xml | 6 +
.../findinfiles/findinfiles.plugin.desktop.in.in | 9 +
.../findinfiles/gedit-findinfiles.metainfo.xml.in | 13 +
plugins/findinfiles/job.vala | 365 ++++++++++++++++++++
plugins/findinfiles/matcher.vala | 126 +++++++
plugins/findinfiles/plugin.vala | 186 ++++++++++
plugins/findinfiles/result-panel.vala | 244 +++++++++++++
po/POTFILES.in | 5 +
po/POTFILES.skip | 5 +
15 files changed, 1337 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cdee919..b4efce4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,9 @@ C_PLUGINS="bookmarks drawspaces wordcompletion"
# Python plugins that don't need special dependencies, besides Python
PY_PLUGINS="bracketcompletion codecomment colorpicker colorschemer commander joinlines multiedit smartspaces
textsize"
+# Vala plugins that don't need special dependencies, besides Vala
+VALA_PLUGINS="findinfiles"
+
PLUGINS="$C_PLUGINS"
disabled_plugins=""
@@ -98,6 +101,33 @@ fi
AM_CONDITIONAL([ENABLE_PYTHON], [test "x$have_python" = "xyes"])
+# Vala
+AC_MSG_CHECKING([whether Vala support is requested])
+AC_ARG_ENABLE([vala],
+ AS_HELP_STRING([--enable-vala], [Enable Vala support]),
+ [enable_vala=$enableval],
+ [enable_vala=auto])
+AC_MSG_RESULT([$enable_vala])
+
+if test "x$enable_vala" != "xno"
+then
+ # This could probably be lower, but let's take the current version
+ VALA_REQUIRED=0.28.0
+ AM_PROG_VALAC([$VALA_REQS])
+ if test "x$VALAC" = "x"; then
+ if test "x$enable_vala" = "xyes"; then
+ AC_MSG_ERROR([Vala support explicitly requested, but not found])
+ fi
+ have_vala=no
+ else
+ have_vala=yes
+ fi
+else
+ have_vala=no
+fi
+
+AM_CONDITIONAL([ENABLE_VALA], [test "x$have_vala" = "xyes"])
+
# zeitgeist (libzeitgeist)
AC_MSG_CHECKING([whether Zeitgeist support is requested])
AC_ARG_ENABLE([zeitgeist],
@@ -224,6 +254,13 @@ AM_CONDITIONAL([ENABLE_DASHBOARD], test "x$have_dashboard" = "xyes")
AM_CONDITIONAL([ENABLE_CHARMAP], test "x$have_gucharmap" = "xyes")
AM_CONDITIONAL([ENABLE_GIT], test "x$have_git2" = "xyes")
+# disable all Vala plugins if there is no Vala support
+if test "x$have_vala" = "xyes"; then
+ PLUGINS="$PLUGINS $VALA_PLUGINS"
+else
+ disabled_plugins="$disabled_plugins $VALA_PLUGINS"
+fi
+
AC_SUBST(PLUGINS)
# ================================================================
@@ -283,6 +320,7 @@ plugins/commander/commander.plugin.desktop.in
plugins/dashboard/dashboard.plugin.desktop.in
plugins/drawspaces/drawspaces.plugin.desktop.in
plugins/drawspaces/org.gnome.gedit.plugins.drawspaces.gschema.xml.in
+plugins/findinfiles/findinfiles.plugin.desktop.in
plugins/git/git.plugin.desktop.in
plugins/joinlines/joinlines.plugin.desktop.in
plugins/multiedit/multiedit.plugin.desktop.in
@@ -305,6 +343,7 @@ Configuration:
Compiler: ${CC}
Prefix: ${prefix}
Python Plugins Support: ${have_python}
+ Vala Plugins Support: ${have_vala}
Plugins:
${PLUGINS}
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 2a1da64..cb431e6 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -22,6 +22,7 @@ include plugins/colorschemer/Makefile.am
include plugins/commander/Makefile.am
include plugins/dashboard/Makefile.am
include plugins/drawspaces/Makefile.am
+include plugins/findinfiles/Makefile.am
include plugins/git/Makefile.am
include plugins/joinlines/Makefile.am
include plugins/multiedit/Makefile.am
diff --git a/plugins/config.vapi b/plugins/config.vapi
new file mode 100644
index 0000000..c07abc0
--- /dev/null
+++ b/plugins/config.vapi
@@ -0,0 +1,6 @@
+[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
+namespace Config {
+ public const string GETTEXT_PACKAGE;
+ public const string GP_LOCALEDIR;
+}
+
diff --git a/plugins/findinfiles/Makefile.am b/plugins/findinfiles/Makefile.am
new file mode 100644
index 0000000..bc7da9a
--- /dev/null
+++ b/plugins/findinfiles/Makefile.am
@@ -0,0 +1,64 @@
+if ENABLE_VALA
+
+plugin_LTLIBRARIES += plugins/findinfiles/libfindinfiles.la
+
+plugins_findinfiles_libfindinfiles_la_CPPFLAGS = \
+ -DGETTEXT_PACKAGE=\""$(GETTEXT_PACKAGE)"\" \
+ -DGP_LOCALEDIR=\""$(localedir)"\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/plugins/findinfiles
+
+plugins_findinfiles_libfindinfiles_la_CFLAGS = \
+ $(GEDIT_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+plugins_findinfiles_libfindinfiles_la_VALAFLAGS = \
+ --vapidir $(top_srcdir)/plugins \
+ --pkg config \
+ --pkg gedit \
+ --pkg PeasGtk-1.0 \
+ --pkg GtkSource-3.0 \
+ --pkg gtk+-3.0 \
+ --pkg gio-2.0 \
+ --pkg posix \
+ --target-glib=2.38 \
+ --gresources=$(top_srcdir)/plugins/findinfiles/findinfiles.gresource.xml
+
+plugins_findinfiles_libfindinfiles_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+plugins_findinfiles_libfindinfiles_la_LIBADD = $(GEDIT_LIBS)
+
+VALASOURCES = \
+ plugins/findinfiles/dialog.vala \
+ plugins/findinfiles/job.vala \
+ plugins/findinfiles/matcher.vala \
+ plugins/findinfiles/plugin.vala \
+ plugins/findinfiles/result-panel.vala
+
+BUILT_SOURCES += \
+ plugins/findinfiles/resources.c
+
+resource_files = $(shell $(GLIB_COMPILE_RESOURCES) \
+ --generate-dependencies \
+ --sourcedir=$(top_srcdir)/plugins/findinfiles/ \
+ $(top_srcdir)/plugins/findinfiles/findinfiles.gresource.xml)
+
+plugins/findinfiles/resources.c: $(top_srcdir)/plugins/findinfiles/findinfiles.gresource.xml
$(resource_files)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(top_srcdir)/plugins/findinfiles/
--generate-source $<
+
+plugins_findinfiles_libfindinfiles_la_SOURCES = \
+ $(VALASOURCES) \
+ $(BUILT_SOURCES)
+
+plugin_in_files += plugins/findinfiles/findinfiles.plugin.desktop.in
+appstream_in_files += plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
+
+EXTRA_DIST += \
+ $(resource_files) \
+ plugins/findinfiles/dialog.ui \
+ plugins/findinfiles/findinfiles.gresource.xml
+
+else
+dist_plugin_in_files += plugins/findinfiles/findinfiles.plugin.desktop.in
+dist_appstream_in_files += plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
+endif
diff --git a/plugins/findinfiles/dialog.ui b/plugins/findinfiles/dialog.ui
new file mode 100644
index 0000000..5376634
--- /dev/null
+++ b/plugins/findinfiles/dialog.ui
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.0"/>
+ <template class="GeditFindInFilesPluginFindDialog" parent="GtkDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Find in Files</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label" translatable="yes">_Close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="no_show_all">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="find_button">
+ <property name="label" translatable="yes" context="label of the find button">_Find</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="search_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes" context="label on the left of the GtkEntry
containing text to search">F_ind </property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="folder_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_In </property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="checkbox_grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkCheckButton" id="match_case_checkbutton">
+ <property name="label" translatable="yes">_Match case</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="entire_word_checkbutton">
+ <property name="label" translatable="yes">Match _entire word only</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="regex_checkbutton">
+ <property name="label" translatable="yes">Re_gular expression</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="cancel">close_button</action-widget>
+ <action-widget response="ok">find_button</action-widget>
+ </action-widgets>
+ </template>
+</interface>
diff --git a/plugins/findinfiles/dialog.vala b/plugins/findinfiles/dialog.vala
new file mode 100644
index 0000000..ab1787a
--- /dev/null
+++ b/plugins/findinfiles/dialog.vala
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2015 The Lemon Man
+*
+* 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 2, 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace Gedit {
+namespace FindInFilesPlugin {
+
+[GtkTemplate (ui = "/org/gnome/gedit/plugins/findinfiles/ui/dialog.ui")]
+class FindDialog : Gtk.Dialog {
+ public Gtk.Entry search_entry;
+ public Gtk.FileChooserButton sel_folder;
+ [GtkChild]
+ public Gtk.CheckButton match_case_checkbutton;
+ [GtkChild]
+ public Gtk.CheckButton entire_word_checkbutton;
+ [GtkChild]
+ public Gtk.CheckButton regex_checkbutton;
+ [GtkChild]
+ public Gtk.Widget find_button;
+ [GtkChild]
+ Gtk.Grid grid;
+ [GtkChild]
+ Gtk.Label search_label;
+ [GtkChild]
+ Gtk.Label folder_label;
+
+ public FindDialog (File? root) {
+ build_layout ();
+ setup_signals ();
+
+ try {
+ if (root != null)
+ sel_folder.set_current_folder_file (root);
+ }
+ catch (Error err) {
+ warning (err.message);
+ }
+ }
+
+ private void build_layout () {
+ search_entry = new Gtk.Entry ();
+ search_entry.set_size_request (300, -1);
+ search_entry.set_hexpand (true);
+ search_entry.set_activates_default (true);
+ grid.attach_next_to (search_entry, search_label, Gtk.PositionType.RIGHT, 1, 1);
+
+ sel_folder = new Gtk.FileChooserButton (_("Select a _folder"), Gtk.FileChooserAction.SELECT_FOLDER);
+ sel_folder.set_hexpand (true);
+ grid.attach_next_to (sel_folder, folder_label, Gtk.PositionType.RIGHT, 1, 1);
+
+ search_label.set_mnemonic_widget (search_entry);
+ folder_label.set_mnemonic_widget (sel_folder);
+
+ set_default_response (Gtk.ResponseType.OK);
+ set_response_sensitive (Gtk.ResponseType.OK, false);
+
+ if (Gtk.Settings.get_default ().gtk_dialogs_use_header) {
+ var header_bar = new Gtk.HeaderBar ();
+
+ header_bar.set_title ("Find in Files");
+ header_bar.set_show_close_button (true);
+
+ this.set_titlebar (header_bar);
+ } else {
+ add_button (_("_Close"), Gtk.ResponseType.CLOSE);
+ }
+ }
+
+ private void setup_signals () {
+ search_entry.changed.connect (() => {
+ find_button.sensitive = (search_entry.text != "");
+ });
+ }
+}
+
+} // namespace FindInFilesPlugin
+} // namespace Gedit
diff --git a/plugins/findinfiles/findinfiles.gresource.xml b/plugins/findinfiles/findinfiles.gresource.xml
new file mode 100644
index 0000000..81da145
--- /dev/null
+++ b/plugins/findinfiles/findinfiles.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/gedit/plugins/findinfiles/ui">
+ <file compressed="true" preprocess="xml-stripblanks">dialog.ui</file>
+ </gresource>
+</gresources>
diff --git a/plugins/findinfiles/findinfiles.plugin.desktop.in.in
b/plugins/findinfiles/findinfiles.plugin.desktop.in.in
new file mode 100644
index 0000000..44ef2a2
--- /dev/null
+++ b/plugins/findinfiles/findinfiles.plugin.desktop.in.in
@@ -0,0 +1,9 @@
+[Plugin]
+Module=findinfiles
+IAge=3
+_Name=Find in Files
+_Description=Find text in all files of a folder.
+_Authors=The Lemon Man <thatlemon at google's mail service dot com>
+Copyright=Copyright © 2015 The Lemon Man
+Website=http://www.gedit.org
+Version= VERSION@
diff --git a/plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
b/plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
new file mode 100644
index 0000000..fb08313
--- /dev/null
+++ b/plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2014 Igor Gnatenko <i gnatenko brain gmail com> -->
+<component type="addon">
+ <id>gedit-findinfiles</id>
+ <extends>org.gnome.gedit.desktop</extends>
+ <_name>Find in Files</_name>
+ <_summary>Find text in all files of a folder</_summary>
+ <url type="homepage">https://wiki.gnome.org/Apps/Gedit/ShippedPlugins</url>
+ <url type="bugtracker">https://bugzilla.gnome.org/enter_bug.cgi?product=gedit&component=Plugins</url>
+ <metadata_license>CC0-1.0</metadata_license>
+ <project_license>GPL-2.0+</project_license>
+ <updatecontact>i gnatenko brain gmail com</updatecontact>
+</component>
diff --git a/plugins/findinfiles/job.vala b/plugins/findinfiles/job.vala
new file mode 100644
index 0000000..a968f93
--- /dev/null
+++ b/plugins/findinfiles/job.vala
@@ -0,0 +1,365 @@
+/*
+* Copyright (C) 2015 The Lemon Man
+*
+* 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 2, 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace Gedit {
+namespace FindInFilesPlugin {
+
+interface IMatcher : Object {
+ public abstract bool has_match (uint8 *text, size_t text_length, size_t pos, ref Range match);
+}
+
+struct Range {
+ size_t from;
+ size_t to;
+}
+
+struct Bookmark {
+ int line_number;
+ size_t line_start;
+ size_t line_length;
+ public static const Bookmark EmptyBookmark = { 0, 0 };
+}
+
+struct Result {
+ string path;
+ size_t line;
+ string context;
+}
+
+class FindJob {
+ public signal void on_match_found (Result result);
+ public signal void on_search_finished ();
+
+ // This queue holds all the file names to scan
+ private AsyncQueue<string> scan_queue = new AsyncQueue<string>();
+
+ // The list of (hard) workers
+ private List<Thread<int>> thread_workers;
+
+ // Count how many workers are still working
+ private uint running_workers = 0;
+
+ private IMatcher? matcher = null;
+
+ private Cancellable cancellable;
+
+ // The actual job parameters
+ public bool include_hidden;
+ public bool match_whole_word;
+ public bool ignore_case;
+ public string? needle { get; private set; }
+
+ int worker () {
+ while (true) {
+ // Wait 0.5 seconds
+ var tv = TimeVal ();
+ tv.add (1000000 / 2);
+
+ var path = scan_queue.timed_pop (ref tv);
+
+ // Check for interruption
+ if (cancellable.is_cancelled ())
+ break;
+
+ // If path is null then we're probably done
+ if (path == null)
+ break;
+
+ // Scan the file
+ scan_file (path);
+ }
+
+ // We're done, check if we're the last worker active and signal it to the user
+ lock (running_workers) {
+ if (0 == (--running_workers)) {
+ // Run the completion callback in the main thread
+ Idle.add (() => { on_search_finished (); return false; });
+ }
+ }
+
+ return 0;
+ }
+
+ public FindJob (Cancellable? cancellable) {
+ this.cancellable = cancellable ?? new Cancellable ();
+ needle = null;
+ include_hidden = false;
+ match_whole_word = false;
+ ignore_case = false;
+ }
+
+ public void prepare (string needle, bool is_regex) throws Error {
+ // Construct the matcher instance
+ if (is_regex) {
+ matcher = new RegexFind (needle, ignore_case);
+ } else {
+ matcher = new BoyerMooreHorspool (needle, ignore_case);
+ }
+ }
+
+ void start_workers_pool () {
+ if (running_workers != 0) {
+ return;
+ }
+
+ var online_cpus = get_num_processors ();
+
+ for (var i = 0; i < online_cpus; i++) {
+ thread_workers.append (new Thread<int> ("Worker %d".printf (i), worker));
+ }
+
+ running_workers = online_cpus;
+ }
+
+ public string extract_context (uint8 *buffer, Range range) {
+ uint8 *slice = new uint8[range.to - range.from];
+ Posix.memcpy (slice, buffer + range.from, range.to - range.from);
+ return Gedit.utils_make_valid_utf8 ((string)slice);
+ }
+
+ public void halt () {
+ if (running_workers == 0)
+ return;
+
+ cancellable.cancel ();
+
+ foreach (var worker in thread_workers) {
+ worker.join ();
+ }
+ }
+
+ bool is_binary (uint8 *buffer, size_t buffer_size) {
+ // Poor-man binary detection, check for NULL bytes
+ return Posix.memchr (buffer, '\0', buffer_size) != null;
+ }
+
+ bool ignore_vcs_dir (FileInfo file) {
+ if (file.get_file_type () == FileType.DIRECTORY) {
+ var name = file.get_name ();
+
+ if (name == ".git" ||
+ name == ".bzr" ||
+ name == ".svn" ||
+ name == ".hg" ||
+ name == "_darcs")
+ return true;
+ }
+
+ return false;
+ }
+
+ public async void execute (string root) throws Error {
+ var queue = new Queue<File> ();
+ var visited = new HashTable<string, bool> (str_hash, str_equal);
+
+ start_workers_pool ();
+
+ queue.push_tail (File.new_for_path (root));
+
+ while (!queue.is_empty ()) {
+ if (cancellable.is_cancelled ())
+ break;
+
+ var dir = queue.pop_head ();
+
+ var e = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME,
+ FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
+ Priority.DEFAULT);
+
+ // Process the entries we got so far
+ while (true) {
+ var files = yield e.next_files_async (100);
+
+ if (files == null)
+ break;
+
+ if (cancellable.is_cancelled ())
+ break;
+
+ foreach (var file in files) {
+ if (!include_hidden && file.get_name ()[0] == '.')
+ continue;
+
+ if (ignore_vcs_dir (file))
+ continue;
+
+ var _file = dir.resolve_relative_path (file.get_name ());
+
+ switch (file.get_file_type ()) {
+ case FileType.REGULAR:
+ scan_queue.push (_file.get_path ());
+ break;
+
+ case FileType.DIRECTORY:
+ queue.push_tail (_file);
+ visited.insert (_file.get_path (), true);
+ break;
+
+ case FileType.SYMBOLIC_LINK:
+ var link_dest = Posix.realpath (_file.get_path ());
+
+ // Follow the link only if its not broken and we didn't visit it
+ if (link_dest != null && !visited.contains (link_dest)) {
+ if (FileUtils.test (link_dest, FileTest.IS_DIR) &&
+ (!ignore_case && link_dest[0] != '.'))
+ queue.push_tail (File.new_for_path (link_dest));
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Returns the line number where the text span starting at 'from' and ending at 'to' lies.
+ void get_line (uint8 *buffer, size_t buffer_size, ref Range span, ref Bookmark bookmark) {
+ // We take an advantage by knowing that all the calls to get_line are sequential, hence
+ // we save the position of the last matched line and start from there
+ var line_count = bookmark.line_number;
+ var line_start = bookmark.line_start;
+
+ var ptr = buffer + bookmark.line_start;
+
+ while (ptr < buffer + buffer_size) {
+ // Find the newline
+ uint8 *nl = Posix.memchr (ptr, '\n', buffer_size - (ptr - buffer));
+
+ if (nl == null) {
+ // No more newlines, consider the trailing NULL as the final newline
+ nl = buffer + buffer_size + 1;
+ } else {
+ // Skip the '\n'
+ nl++;
+ }
+
+ var line_length = nl - ptr;
+
+ // Check if the match is within this line
+ if (span.from >= line_start && span.to < line_start + line_length) {
+ // Update the bookmark
+ bookmark.line_number = line_count;
+ bookmark.line_start = line_start;
+ bookmark.line_length = line_length - 1;
+
+ return;
+ }
+
+ line_count++;
+ line_start += line_length;
+
+ ptr = nl;
+ }
+
+ // There's no way to exit the loop, unless there's a problem in the logic above
+ assert_not_reached ();
+ }
+
+ bool is_word_boundary (uint8 *buf, size_t buf_size, size_t from, size_t to) {
+ unichar ch;
+ bool prev, next;
+ unowned string str;
+
+ assert (to > from && to <= buf_size);
+
+ // There's not much we can do
+ if (to - from > int.MAX)
+ return false;
+
+ // Silence the warning about ch being uninitialized
+ ch = '\0';
+
+ prev = next = true;
+ str = (string)(buf + from);
+
+ // Those are being modified by the get_{prev,next}_char calls
+ int start = 0;
+ int end = (int)(to - from);
+
+ // The match is on a word boundary if there are no consecutive alphanumeric
+ // characters right before or after the match
+ var head = str.get_char (0);
+ if (start > 0 && str.get_prev_char (ref start, out ch))
+ prev = head.isalnum () != ch.isalnum ();
+
+ var tail = str.get_char (end - 1);
+ if (end < buf_size && str.get_next_char (ref end, out ch))
+ next = tail.isalnum () != ch.isalnum ();
+
+ return prev && next;
+ }
+
+ void scan_file (string path) {
+ MappedFile file;
+
+ try {
+ file = new MappedFile (path, false);
+ }
+ catch (FileError err) {
+ warning (err.message);
+ return;
+ }
+
+ var buffer_size = file.get_length ();
+ var buffer = (uint8 *)file.get_contents ();
+
+ // Skip binary files for obvious reasons
+ if (is_binary (buffer, buffer_size))
+ return;
+
+ Range match = { 0, 0 };
+ Bookmark bookmark = Bookmark.EmptyBookmark;
+ var last_line = -1;
+
+ for (size_t buffer_pos = 0; buffer_pos < buffer_size; ) {
+ if (cancellable.is_cancelled ())
+ break;
+
+ // Exit when there's no match
+ if (!matcher.has_match (buffer, buffer_size, buffer_pos, ref match))
+ break;
+
+ // Check if the match lies on a word boundary
+ if (match_whole_word) {
+ if (!is_word_boundary (buffer, buffer_size, (int)match.from, (int)match.to)) {
+ buffer_pos = match.to;
+ continue;
+ }
+ }
+
+ get_line (buffer, buffer_size, ref match, ref bookmark);
+
+ // Find out what line the match lies in
+ var match_line = 1 + bookmark.line_number;
+
+ // Notify that we got a match
+ if (last_line != match_line) {
+ Result res = { path, match_line, extract_context (buffer, match) };
+ on_match_found (res);
+ }
+
+ last_line = match_line;
+
+ // Keep searching past the match
+ buffer_pos = match.to;
+ }
+ }
+}
+
+} // namespace FindInFilesPlugin
+} // namespace Gedit
diff --git a/plugins/findinfiles/matcher.vala b/plugins/findinfiles/matcher.vala
new file mode 100644
index 0000000..3192b50
--- /dev/null
+++ b/plugins/findinfiles/matcher.vala
@@ -0,0 +1,126 @@
+/*
+* Copyright (C) 2015 The Lemon Man
+*
+* 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 2, 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace Gedit {
+namespace FindInFilesPlugin {
+
+class RegexFind : Object, IMatcher {
+ private Regex re;
+
+ public RegexFind (string pattern, bool ignore_case) throws Error {
+ var flags = RegexCompileFlags.OPTIMIZE;
+
+ if (ignore_case)
+ flags |= RegexCompileFlags.CASELESS;
+
+ re = new Regex (pattern, flags);
+ }
+
+ public bool has_match (uint8 *text, size_t text_length, size_t pos, ref Range match) {
+ MatchInfo info;
+ int casted_pos;
+
+ // Prevent an integer overflow when downcasting from size_t to int
+ if (pos > int.MAX) {
+ casted_pos = 0;
+ text += pos;
+ } else {
+ casted_pos = (int)pos;
+ }
+
+ // Avoid strdup-ing the whole buffer
+ unowned string str = (string)text;
+
+ // Pass the text length as str isn't null terminated
+ try {
+ if (!re.match_full (str, (ssize_t)text_length, casted_pos, 0, out info))
+ return false;
+ }
+ catch (RegexError err) {
+ warning (err.message);
+ return false;
+ }
+
+ info.fetch_pos (0, out match.from, out match.to);
+
+ return true;
+ }
+}
+
+class BoyerMooreHorspool : Object, IMatcher {
+ private string pattern;
+ private int bad_char_shift[256];
+
+ private bool ignore_case;
+
+ public BoyerMooreHorspool (string pattern_, bool ignore_case_) {
+ pattern = pattern_;
+ ignore_case = ignore_case_;
+
+ for (int i = 0; i < 256; i++) {
+ bad_char_shift[i] = pattern.length;
+ }
+
+ for (int i = 0; i < pattern.length - 1; i++) {
+ if (ignore_case) {
+ bad_char_shift[Posix.toupper(pattern[i])] = pattern.length - 1 - i;
+ bad_char_shift[Posix.tolower(pattern[i])] = pattern.length - 1 - i;
+ } else {
+ bad_char_shift[pattern[i]] = pattern.length - 1 - i;
+ }
+ }
+ }
+
+ public bool has_match (uint8 *text, size_t text_length, size_t pos, ref Range match) {
+ uint i = 0;
+
+ text += pos;
+ text_length -= pos;
+
+ if (text_length < pattern.length)
+ return false;
+
+ while (i <= text_length - pattern.length) {
+ for (int j = pattern.length - 1; j >= 0; j--) {
+ // Check for a match backwards
+ if (ignore_case) {
+ if (Posix.tolower(text[i + j]) != Posix.tolower(pattern[j]))
+ break;
+ } else {
+ if (text[i + j] != pattern[j])
+ break;
+ }
+
+ // The whole needle has been matched!
+ if (j == 0) {
+ match.from = pos + i;
+ match.to = match.from + pattern.length;
+ return true;
+ }
+ }
+
+ // Jump ahead in the buffer
+ i += bad_char_shift[text[i + pattern.length - 1]];
+ }
+
+ return false;
+ }
+}
+
+} // namespace FindInFilesPlugin
+} // namespace Gedit
diff --git a/plugins/findinfiles/plugin.vala b/plugins/findinfiles/plugin.vala
new file mode 100644
index 0000000..7f7fe0e
--- /dev/null
+++ b/plugins/findinfiles/plugin.vala
@@ -0,0 +1,186 @@
+/*
+* Copyright (C) 2015 The Lemon Man
+*
+* 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 2, 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+namespace Gedit {
+namespace FindInFilesPlugin {
+
+public class Window : Gedit.WindowActivatable, Peas.ExtensionBase {
+ public Window () {
+ GLib.Object ();
+ }
+
+ public Gedit.Window window {
+ owned get; construct;
+ }
+
+ File? get_file_browser_root () {
+ var bus = window.get_message_bus ();
+
+ if (bus.is_registered ("/plugins/filebrowser", "get_root")) {
+ var msg = Object.new (bus.lookup ("/plugins/filebrowser", "get_root"),
+ "method", "get_root",
+ "object_path", "/plugins/filebrowser");
+
+ bus.send_message_sync (msg as Gedit.Message);
+
+ Value val = Value (typeof (Object));
+ msg.get_property ("location", ref val);
+
+ return val.dup_object () as File;
+ }
+
+ return null;
+ }
+
+ void dialog_run () {
+ var active_doc = window.get_active_document ();
+
+ // Use the filebrowser root as starting folder, if possible.
+ var root = get_file_browser_root ();
+
+ // If that's not possible try to use the current document folder.
+ if (root == null) {
+ var location = active_doc.get_file ().get_location ();
+ if (location != null)
+ root = location.get_parent ();
+ }
+
+ // Fall back to the user's root if none of the methods were successfully
+ if (root == null)
+ root = File.new_for_path (Environment.get_home_dir ());
+
+ var dialog = new FindDialog (root);
+
+ dialog.set_transient_for (window);
+ dialog.set_destroy_with_parent (true);
+
+ // Grab the selection and use it as search query
+ Gtk.TextIter start, end;
+ if (active_doc.get_selection_bounds (out start, out end)) {
+ var selection = active_doc.get_text (start, end, true);
+
+ dialog.search_entry.text = Gtk.SourceUtils.escape_search_text (selection);
+ }
+
+ dialog.response.connect ((response_id) => {
+ if (response_id == Gtk.ResponseType.OK) {
+ var search_text = dialog.search_entry.text;
+ var search_path = dialog.sel_folder.get_filename ();
+
+ // Make sure there's no other search with the same parameters
+ var panel = (Gtk.Stack)window.get_bottom_panel ();
+ var child = panel.get_child_by_name ("find-in-files");
+ if (child != null) {
+ child.destroy ();
+ }
+
+ // Setup the job parameters
+ var cancellable = new Cancellable ();
+ var job = new FindJob (cancellable);
+
+ job.ignore_case = !dialog.match_case_checkbutton.active;
+ job.match_whole_word = dialog.entire_word_checkbutton.active;
+
+ try {
+ job.prepare (dialog.search_entry.text, dialog.regex_checkbutton.active);
+ job.execute.begin (search_path);
+ }
+ catch (Error err) {
+ warning (err.message);
+ dialog.destroy ();
+ return;
+ }
+
+ // Prepare the panel to hold the results
+ var result_panel = new ResultPanel.for_job (job, search_path, window);
+
+ panel.add_titled (result_panel, "find-in-files", "\"%s\"".printf(search_text));
+
+ result_panel.show_all ();
+
+ // Make the panel visible
+ panel.set_visible (true);
+
+ // Focus the new search tab
+ panel.set_visible_child_name ("find-in-files");
+
+ result_panel.toggle_stop_button (true);
+ result_panel.grab_focus ();
+ }
+
+ dialog.destroy ();
+ });
+
+ dialog.show_all ();
+ }
+
+ public void activate () {
+ var act = new SimpleAction ("find-in-files", null);
+ window.add_action (act);
+ act.activate.connect (dialog_run);
+ }
+
+ public void deactivate () {
+ }
+
+ public void update_state () {
+ }
+}
+
+public class App : GLib.Object, Gedit.AppActivatable {
+ private Gedit.MenuExtension? menu_ext = null;
+
+ public App () {
+ GLib.Object ();
+ }
+
+ public Gedit.App app {
+ owned get; construct;
+ }
+
+ public void activate () {
+ menu_ext = extend_menu ("search-section");
+
+ var item = new GLib.MenuItem (_("Find in Files..."), "win.find-in-files");
+ menu_ext.append_menu_item (item);
+
+ app.add_accelerator ("<Shift><Ctrl>f", "win.find-in-files", null);
+ }
+
+ public void deactivate () {
+ menu_ext.remove_items ();
+
+ app.remove_accelerator ("win.find-in-files", null);
+ }
+}
+
+} // namespace FindInFilesPlugin
+} // namespace Gedit
+
+[ModuleInit]
+public void peas_register_types (TypeModule module)
+{
+ var objmodule = module as Peas.ObjectModule;
+
+ Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GP_LOCALEDIR);
+
+ objmodule.register_extension_type (typeof (Gedit.WindowActivatable),
+ typeof (Gedit.FindInFilesPlugin.Window));
+ objmodule.register_extension_type (typeof (Gedit.AppActivatable),
+ typeof (Gedit.FindInFilesPlugin.App));
+}
diff --git a/plugins/findinfiles/result-panel.vala b/plugins/findinfiles/result-panel.vala
new file mode 100644
index 0000000..19a7a38
--- /dev/null
+++ b/plugins/findinfiles/result-panel.vala
@@ -0,0 +1,244 @@
+/*
+* Copyright (C) 2015 The Lemon Man
+*
+* 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 2, 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+using Gtk;
+
+namespace Gedit {
+namespace FindInFilesPlugin {
+
+void toggle_fold (TreeView tv, TreePath path) {
+ if (tv.is_row_expanded (path))
+ tv.collapse_row (path);
+ else
+ tv.expand_row (path, false);
+}
+
+class ResultPanel : Overlay {
+ // The search job whose results we are showing
+ private FindJob job;
+
+ private string root;
+ private TreeView list;
+ private TreeStore results_model;
+ private Button stop_button;
+ private Gedit.Window win;
+
+ ~ResultPanel () {
+ job.halt ();
+ }
+
+ public new void grab_focus () {
+ list.grab_focus ();
+ }
+
+ // Try to find 'key' anywhere in the path
+ private bool list_search (TreeModel model, int column, string key, TreeIter iter) {
+ Value val;
+
+ model.get_value (iter, 0, out val);
+
+ // The return values are actually swapped
+ return (Posix.strstr (val.get_string (), key) != null) ? false : true;
+ }
+
+ private bool on_button_press (Gdk.EventButton event) {
+ // Create and show the popup menu on right click
+ if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3) {
+ var popup = new Gtk.Menu ();
+ var close_item = new Gtk.MenuItem.with_mnemonic (_("_Close"));
+
+ close_item.activate.connect (() => {
+ // Easy peasy
+ this.destroy ();
+ });
+
+ popup.attach_to_widget (this, null);
+ popup.add (close_item);
+ popup.show_all ();
+ popup.popup (null, null, null, event.button, event.time);
+
+ return true;
+ }
+ return false;
+ }
+
+ private void on_row_activated (TreePath path, TreeViewColumn column) {
+ TreeIter iter;
+ TreeIter parent;
+
+ if (!results_model.get_iter (out iter, path))
+ return;
+
+ // This happens when the user clicks on the file entry
+ if (!results_model.iter_parent (out parent, iter)) {
+ toggle_fold (list, path);
+ return;
+ }
+
+ Value val0, val1;
+ results_model.get_value (parent, 0, out val0);
+ results_model.get_value (iter, 1, out val1);
+
+ var selection_path = val0.get_string (),
+ selection_line = val1.get_int ();
+
+ Gedit.commands_load_location (win,
+ File.new_for_path (selection_path),
+ null,
+ selection_line,
+ 0);
+ }
+
+ string get_relative_path (string path, string from) {
+ // Simplistic way to get a relative path, strip the leading slash too
+ if (path.has_prefix (from))
+ return path.substring (from.length + 1);
+
+ return path;
+ }
+
+ void column_data_func (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) {
+ TreeIter parent;
+
+ if (!results_model.iter_parent (out parent, iter)) {
+ Value val0, val1;
+
+ model.get_value (iter, 0, out val0);
+ model.get_value (iter, 1, out val1);
+
+ var path = val0.get_string ();
+ var hits = val1.get_int ();
+
+ (cell as CellRendererText).markup = "<b>%s</b> (%d %s)".printf (
+ get_relative_path (path, root),
+ hits,
+ (hits == 1) ? "hit" : "hits");
+ } else {
+ Value val0, val1;
+
+ model.get_value (iter, 0, out val0);
+ model.get_value (iter, 1, out val1);
+
+ var at = val1.get_int ();
+ var line = val0.get_string ();
+
+ (cell as CellRendererText).text = "%d:%s".printf (at, line);
+ }
+ }
+
+ public ResultPanel.for_job (FindJob job_, string root_, Gedit.Window win_) {
+ results_model = new TreeStore (2, typeof (string), typeof (int));
+ job = job_;
+ win = win_;
+ root = root_;
+
+ var it_table = new HashTable<string, TreeIter?> (str_hash, str_equal);
+
+ // Connect to the job signals
+ job.on_match_found.connect((result) => {
+ // The 'on_match_found' is emitted from the worker thread
+ Idle.add (() => {
+ TreeIter iter;
+
+ var parent = it_table.lookup (result.path);
+
+ // Create a new top-level node for the file
+ if (parent == null) {
+ results_model.append (out parent, null);
+ results_model.set (parent,
+ 0, result.path,
+ 1, 0);
+
+ it_table.insert (result.path, parent);
+ }
+
+ // Increment the hits counter
+ Value val0;
+ results_model.get_value (parent, 1, out val0);
+ results_model.set (parent, 1, val0.get_int () + 1);
+
+ // Append the result
+ results_model.append (out iter, parent);
+ results_model.set (iter,
+ 0, result.context,
+ 1, result.line);
+
+ return false;
+ });
+ });
+
+ job.on_search_finished.connect (() => {
+ job.halt ();
+ stop_button.set_visible (false);
+
+ list.expand_all ();
+
+ TreeIter dummy;
+ // Check if the search gave no results
+ if (!results_model.get_iter_first (out dummy)) {
+ results_model.append (out dummy, null);
+ results_model.set (dummy, 0, _("No results found"));
+ }
+ });
+
+ // Create the ui to hold the results
+ list = new TreeView.with_model (results_model);
+
+ list.set_search_column (0);
+ list.set_search_equal_func (list_search);
+
+ list.insert_column_with_data_func (-1,
+ "File",
+ new CellRendererText (),
+ column_data_func);
+
+ // list.set_activate_on_single_click (true);
+
+ list.row_activated.connect (on_row_activated);
+ list.button_press_event.connect (on_button_press);
+
+ // The stop button is showed in the bottom-left corner of the TreeView
+ stop_button = new Button.from_icon_name ("process-stop-symbolic", IconSize.BUTTON);
+ stop_button.set_tooltip_text ("Stop the search");
+ stop_button.set_visible (false);
+ stop_button.set_valign (Align.END);
+ stop_button.set_halign (Align.END);
+ stop_button.set_margin_bottom (4);
+ stop_button.set_margin_end (4);
+
+ stop_button.clicked.connect (() => {
+ stop_button.set_visible (false);
+ job.halt ();
+ });
+
+ var scroll = new ScrolledWindow (null, null);
+ scroll.set_policy (PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
+ scroll.add (list);
+
+ // The overlay contains the results list and the stop button
+ this.add_overlay (stop_button);
+ this.add (scroll);
+ }
+
+ public void toggle_stop_button (bool show) {
+ stop_button.set_visible (show);
+ }
+}
+
+} // namespace FindInFilesPlugin
+} // namespace Gedit
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c47c692..a83fd38 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -29,6 +29,11 @@ plugins/drawspaces/gedit-drawspaces.metainfo.xml.in
plugins/drawspaces/gedit-drawspaces-app-activatable.c
[type: gettext/glade]plugins/drawspaces/gedit-drawspaces-configurable.ui
plugins/drawspaces/org.gnome.gedit.plugins.drawspaces.gschema.xml.in.in
+plugins/findinfiles/dialog.vala
+plugins/findinfiles/plugin.vala
+plugins/findinfiles/result-panel.vala
+plugins/findinfiles/findinfiles.plugin.desktop.in.in
+[type: gettext/glade]plugins/findinfiles/dialog.ui
plugins/git/git.plugin.desktop.in.in
plugins/git/gedit-git.metainfo.xml.in
plugins/joinlines/joinlines.plugin.desktop.in.in
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index b2d14ff..37a4fd3 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -8,6 +8,11 @@ plugins/commander/commander.plugin.desktop.in
plugins/dashboard/dashboard.plugin.desktop.in
plugins/drawspaces/drawspaces.plugin.desktop.in
plugins/drawspaces/org.gnome.gedit.plugins.drawspaces.gschema.xml.in
+plugins/findinfiles/dialog.c
+plugins/findinfiles/findinfiles.plugin.desktop.in
+plugins/findinfiles/gedit-findinfiles.metainfo.xml.in
+plugins/findinfiles/plugin.c
+plugins/findinfiles/result-panel.c
plugins/git/git.plugin.desktop.in
plugins/joinlines/joinlines.plugin.desktop.in
plugins/multiedit/multiedit.plugin.desktop.in
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]