[gedit-plugins] Add "Find In Files" plugin



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&amp;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]