diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2014-11-22 05:31:31 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2014-11-22 05:31:31 +0100 |
commit | 427c9d16ce7e62cbe2671748cd8434132ce60482 (patch) | |
tree | b3b4657c8178dcc9308790abaa1c2cf9bbc4c09b | |
parent | 28fd3dbdc831f28c91d521e1804a9d9ff5bbf3db (diff) | |
download | sciteco-427c9d16ce7e62cbe2671748cd8434132ce60482.tar.gz |
added globbing command EN
* implements the same globbing as the EB command already did
* uses Globber helper class that behaves more like UNIX glob().
glib only has a glob-style pattern matcher.
* The Globber class may be extended later to provide more
UNIX-like globbing.
* lexer.tes has been updated to make use of globbing.
Now, lexers can be automatically loaded and registered at
startup. To install a new lexer, it's sufficient to copy
a file to the lexers/ directory.
-rw-r--r-- | lib/lexer.tes | 21 | ||||
-rw-r--r-- | lib/lexers/batch.tes | 4 | ||||
-rw-r--r-- | lib/lexers/cmake.tes | 4 | ||||
-rw-r--r-- | lib/lexers/cpp.tes | 4 | ||||
-rw-r--r-- | lib/lexers/make.tes | 4 | ||||
-rw-r--r-- | lib/lexers/patch.tes | 4 | ||||
-rw-r--r-- | lib/lexers/sh.tes | 4 | ||||
-rw-r--r-- | lib/lexers/xml.tes | 4 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cmdline.cpp | 1 | ||||
-rw-r--r-- | src/glob.cpp | 152 | ||||
-rw-r--r-- | src/glob.h | 66 | ||||
-rw-r--r-- | src/parser.cpp | 2 | ||||
-rw-r--r-- | src/ring.cpp | 41 | ||||
-rw-r--r-- | src/ring.h | 6 |
15 files changed, 244 insertions, 74 deletions
diff --git a/lib/lexer.tes b/lib/lexer.tes index 01c3bf1..0a4622c 100644 --- a/lib/lexer.tes +++ b/lib/lexer.tes @@ -19,14 +19,19 @@ :M[color.error],35M[color.set] } -! individual tests are appended by the lexers/*.tes files ! -EMQ[$SCITECOPATH]/lexers/batch.tes -EMQ[$SCITECOPATH]/lexers/cmake.tes -EMQ[$SCITECOPATH]/lexers/cpp.tes -EMQ[$SCITECOPATH]/lexers/make.tes -EMQ[$SCITECOPATH]/lexers/patch.tes -EMQ[$SCITECOPATH]/lexers/sh.tes -EMQ[$SCITECOPATH]/lexers/xml.tes +! Automatically mung all the lexers and them to "lexer.auto" ! +Q*U.#cd EQ.[lexers] +ENQ[$SCITECOPATH]/lexers/*.tes J +<:L;R + 0X.[filename] .-4U.p -S/ .,Q.pX.[name] + EMQ.[filename] + EQ[lexer.auto] ZJ + @I{ + :M[lexer.test.Q.[name]]"S :M[lexer.set.Q.[name]] Oend ' + } + EQ.[lexers] +L> +Q.#cdEB ! append an "end" label ! :[lexer.auto]!end! diff --git a/lib/lexers/batch.tes b/lib/lexers/batch.tes index 7175a6c..000910d 100644 --- a/lib/lexers/batch.tes +++ b/lib/lexers/batch.tes @@ -21,7 +21,3 @@ :M[color.variable],6M[color.set] :M[color.operator],7M[color.set] } - -@:[lexer.auto]{ - :M[lexer.test.batch]"S :M[lexer.set.batch] Oend ' -} diff --git a/lib/lexers/cmake.tes b/lib/lexers/cmake.tes index 435a357..02728e5 100644 --- a/lib/lexers/cmake.tes +++ b/lib/lexers/cmake.tes @@ -75,7 +75,3 @@ :M[color.variable],13M[color.set] :M[color.number],14M[color.set] } - -@:[lexer.auto]{ - :M[lexer.test.cmake]"S :M[lexer.set.cmake] Oend ' -} diff --git a/lib/lexers/cpp.tes b/lib/lexers/cpp.tes index f07555a..8d7cee8 100644 --- a/lib/lexers/cpp.tes +++ b/lib/lexers/cpp.tes @@ -37,7 +37,3 @@ :M[color.preproc],9M[color.set] :M[color.operator],10M[color.set] } - -@:[lexer.auto]{ - :M[lexer.test.cpp]"S :M[lexer.set.cpp] Oend ' -} diff --git a/lib/lexers/make.tes b/lib/lexers/make.tes index 48ad886..bf7a973 100644 --- a/lib/lexers/make.tes +++ b/lib/lexers/make.tes @@ -13,7 +13,3 @@ :M[color.target],5M[color.set] :M[color.error],6M[color.set] } - -@:[lexer.auto]{ - :M[lexer.test.make]"S :M[lexer.set.make] Oend ' -} diff --git a/lib/lexers/patch.tes b/lib/lexers/patch.tes index 2a7204d..20670c1 100644 --- a/lib/lexers/patch.tes +++ b/lib/lexers/patch.tes @@ -14,7 +14,3 @@ :M[color.addition],6M[color.set] :M[color.change],7M[color.set] } - -@:[lexer.auto]{ - :M[lexer.test.patch]"S :M[lexer.set.patch] Oend ' -} diff --git a/lib/lexers/sh.tes b/lib/lexers/sh.tes index 7d4f14c..5e853d0 100644 --- a/lib/lexers/sh.tes +++ b/lib/lexers/sh.tes @@ -48,7 +48,3 @@ :M[color.variable],10M[color.set] :M[color.string2],11M[color.set] ! Backticks ! } - -@:[lexer.auto]{ - :M[lexer.test.sh]"S :M[lexer.set.sh] Oend ' -} diff --git a/lib/lexers/xml.tes b/lib/lexers/xml.tes index de82542..30094ea 100644 --- a/lib/lexers/xml.tes +++ b/lib/lexers/xml.tes @@ -23,7 +23,3 @@ :M[color.preproc2],13M[color.set] ! PI ! :M[color.string2],14M[color.set] ! CDATA ! } - -@:[lexer.auto]{ - :M[lexer.test.xml]"S :M[lexer.set.xml] Oend ' -} diff --git a/src/Makefile.am b/src/Makefile.am index af324e0..d02c0b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,7 @@ libsciteco_base_a_SOURCES = main.cpp sciteco.h \ parser.cpp parser.h \ search.cpp search.h \ spawn.cpp spawn.h \ + glob.cpp glob.h \ goto.cpp goto.h \ rbtree.cpp rbtree.h \ symbols.cpp symbols.h \ diff --git a/src/cmdline.cpp b/src/cmdline.cpp index e78db60..8b2d412 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -36,6 +36,7 @@ #include "undo.h" #include "symbols.h" #include "spawn.h" +#include "glob.h" #include "error.h" #include "cmdline.h" diff --git a/src/glob.cpp b/src/glob.cpp new file mode 100644 index 0000000..71100b7 --- /dev/null +++ b/src/glob.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012-2014 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <glib.h> + +#include "sciteco.h" +#include "interface.h" +#include "parser.h" +#include "ring.h" +#include "glob.h" + +namespace SciTECO { + +namespace States { + StateGlob glob; +} + +Globber::Globber(const gchar *_pattern) +{ + gchar *basename; + + dirname = g_path_get_dirname(_pattern); + dir = g_dir_open(dirname, 0, NULL); + + basename = g_path_get_basename(_pattern); + pattern = g_pattern_spec_new(basename); + g_free(basename); +} + +gchar * +Globber::next(void) +{ + const gchar *basename; + + while ((basename = g_dir_read_name(dir))) + if (g_pattern_match_string(pattern, basename)) + return g_build_filename(dirname, basename, NIL); + + return NULL; +} + +Globber::~Globber() +{ + if (pattern) + g_pattern_spec_free(pattern); + if (dir) + g_dir_close(dir); + g_free(dirname); +} + +/* + * Command States + */ + +/*$ + * EN[pattern]$ -- Get list of files matching a glob pattern + * + * The EN command expands a glob \fIpattern\fP to a list of + * matching file names. This is similar to globbing + * on UNIX but not as powerful. + * A \fIpattern\fP is a file name with \(lq*\(rq and + * \(lq?\(rq wildcards: + * \(lq*\(rq matches an arbitrary, possibly empty, string. + * \(lq?\(rq matches an arbitrary character. + * + * EN will currently only match files in the file name component + * of \fIpattern\fP, not on each component of the path name + * separately. + * In other words, EN only looks through the directory + * of \fIpattern\fP \(em you cannot effectively match + * multiple directories. + * + * The result of the globbing is inserted into the current + * document, at the current position. + * A linefeed is inserted after every file name, i.e. + * every matching file will be on its own line. + * + * String-building characters are enabled for EN. + */ +/* + * NOTE: This does not work like classic TECO's + * EN command (iterative globbing), since the + * position in the directory cannot be reasonably + * reset on rubout with glib's API. + * If we have to perform all the globbing on initialization + * we can just as well return all the results at once. + * And we can add them to the current document since + * when they should be in a register, the user will + * have to edit that register anyway. + */ +State * +StateGlob::done(const gchar *str) +{ + BEGIN_EXEC(&States::start); + + Globber globber(str); + + gchar *filename; + bool text_added = false; + + interface.ssm(SCI_BEGINUNDOACTION); + + while ((filename = globber.next())) { + size_t len = strlen(filename); + /* overwrite trailing null */ + filename[len] = '\n'; + + /* + * FIXME: Once we're 8-bit clean, we should + * add the filenames null-terminated + * (there may be linebreaks in filename). + */ + interface.ssm(SCI_ADDTEXT, len+1, + (sptr_t)filename); + + g_free(filename); + text_added = true; + } + + interface.ssm(SCI_SCROLLCARET); + interface.ssm(SCI_ENDUNDOACTION); + + if (text_added) { + ring.dirtify(); + if (current_doc_must_undo()) + interface.undo_ssm(SCI_UNDO); + } + + return &States::start; +} + +} /* namespace SciTECO */ diff --git a/src/glob.h b/src/glob.h new file mode 100644 index 0000000..6652d1d --- /dev/null +++ b/src/glob.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012-2014 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GLOB_H +#define __GLOB_H + +#include <string.h> + +#include <glib.h> + +#include "sciteco.h" +#include "parser.h" + +namespace SciTECO { + +/* + * Auxiliary functions + */ +static inline bool +is_glob_pattern(const gchar *str) +{ + return strchr(str, '*') || strchr(str, '?'); +} + +class Globber { + gchar *dirname; + GDir *dir; + GPatternSpec *pattern; + +public: + Globber(const gchar *pattern); + ~Globber(); + + gchar *next(void); +}; + +/* + * Command states + */ + +class StateGlob : public StateExpectFile { +private: + State *done(const gchar *str); +}; + +namespace States { + extern StateGlob glob; +} + +} /* namespace SciTECO */ + +#endif diff --git a/src/parser.cpp b/src/parser.cpp index 3c991fc..6995a6b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -37,6 +37,7 @@ #include "symbols.h" #include "search.h" #include "spawn.h" +#include "glob.h" #include "cmdline.h" #include "error.h" @@ -1801,6 +1802,7 @@ StateECommand::StateECommand() : State() transitions['C'] = &States::executecommand; transitions['G'] = &States::egcommand; transitions['M'] = &States::macro_file; + transitions['N'] = &States::glob; transitions['S'] = &States::scintilla_symbols; transitions['Q'] = &States::eqcommand; transitions['W'] = &States::savefile; diff --git a/src/ring.cpp b/src/ring.cpp index 575d0e7..03cda37 100644 --- a/src/ring.cpp +++ b/src/ring.cpp @@ -38,8 +38,9 @@ #include "parser.h" #include "expressions.h" #include "qregisters.h" -#include "ring.h" +#include "glob.h" #include "error.h" +#include "ring.h" #ifdef HAVE_WINDOWS_H /* here it shouldn't cause conflicts with other headers */ @@ -600,8 +601,10 @@ StateEditFile::do_edit(tecoInt id) * Naturally this only has any effect in interactive * mode. * - * <file> may also be a glob-pattern, in which case + * <file> may also be a glob pattern, in which case * all files matching the pattern are opened/edited. + * Globbing is performed exactly the same as the + * EN command does. * * File names of buffers in the ring are normalized * by making them absolute. @@ -661,37 +664,11 @@ StateEditFile::done(const gchar *str) } if (is_glob_pattern(str)) { - gchar *dirname; - GDir *dir; - - dirname = g_path_get_dirname(str); - dir = g_dir_open(dirname, 0, NULL); - - if (dir) { - const gchar *basename; - GPatternSpec *pattern; - - basename = g_path_get_basename(str); - pattern = g_pattern_spec_new(basename); - g_free((gchar *)basename); - - while ((basename = g_dir_read_name(dir))) { - if (g_pattern_match_string(pattern, basename)) { - gchar *filename; - - filename = g_build_filename(dirname, - basename, - NIL); - do_edit(filename); - g_free(filename); - } - } - - g_pattern_spec_free(pattern); - g_dir_close(dir); - } + Globber globber(str); + gchar *filename; - g_free(dirname); + while ((filename = globber.next())) + do_edit(filename); } else { do_edit(*str ? str : NULL); } @@ -18,7 +18,6 @@ #ifndef __RING_H #define __RING_H -#include <string.h> #include <bsd/sys/queue.h> #include <glib.h> @@ -38,11 +37,6 @@ namespace SciTECO { /* * Auxiliary functions */ -static inline bool -is_glob_pattern(const gchar *str) -{ - return strchr(str, '*') || strchr(str, '?'); -} /* * Get absolute/full version of a possibly relative path. |