comparison scripts/help/lookfor.m @ 20135:4c6d70a35192

lookfor.m: Overhaul function and fix bug #44924. * lookfor.m: Rewrite docstring. Match variable names in function prototype to docstring. Use more meaningful variable names throughout function. Don't continue to process a function if get_first_help_sentence returns an error (bug #44924). Iterate directly over cell list rather than using a for loop with a range to index into cell list.
author Rik <rik@octave.org>
date Sun, 26 Apr 2015 21:14:53 -0700
parents 4197fc428c7d
children 7503499a252b
comparison
equal deleted inserted replaced
20134:57b6e06ed811 20135:4c6d70a35192
17 ## <http://www.gnu.org/licenses/>. 17 ## <http://www.gnu.org/licenses/>.
18 18
19 ## -*- texinfo -*- 19 ## -*- texinfo -*-
20 ## @deftypefn {Command} {} lookfor @var{str} 20 ## @deftypefn {Command} {} lookfor @var{str}
21 ## @deftypefnx {Command} {} lookfor -all @var{str} 21 ## @deftypefnx {Command} {} lookfor -all @var{str}
22 ## @deftypefnx {Function File} {[@var{func}, @var{helpstring}] =} lookfor (@var{str}) 22 ## @deftypefnx {Function File} {[@var{fcn}, @var{help1str}] =} lookfor (@var{str})
23 ## @deftypefnx {Function File} {[@var{func}, @var{helpstring}] =} lookfor ("-all", @var{str}) 23 ## @deftypefnx {Function File} {[@var{fcn}, @var{help1str}] =} lookfor ("-all", @var{str})
24 ## Search for the string @var{str} in all functions found in the current 24 ## Search for the string @var{str} in all functions using the current function
25 ## function search path. By default, @code{lookfor} searches for @var{str} 25 ## search path.
26 ## in the first sentence of the help string of each function found. The entire
27 ## help text of each function can be searched if the @qcode{"-all"} argument is
28 ## supplied. All searches are case insensitive.
29 ## 26 ##
30 ## Called with no output arguments, @code{lookfor} prints the list of 27 ## By default, @code{lookfor} looks for @var{str} in the first sentence of the
31 ## matching functions to the terminal. Otherwise, the output arguments 28 ## help string for each function found. The entire help text of each function
32 ## @var{func} and @var{helpstring} define the matching functions and the 29 ## can be searched by using the @qcode{"-all"} argument. All searches are case
33 ## first sentence of each of their help strings. 30 ## insensitive.
34 ## 31 ##
35 ## The ability of @code{lookfor} to correctly identify the first 32 ## When called with no output arguments, @code{lookfor} prints the list of
36 ## sentence of the help text is dependent on the format of the 33 ## matching functions to the terminal. Otherwise, the output argument
37 ## function's help. All Octave core functions are correctly 34 ## @var{fcns} contains the function names and @var{help1str} contains the first
38 ## formatted, but the same can not be guaranteed for external packages and 35 ## sentence from the help string of each function.
39 ## user-supplied functions. Therefore, the use of the @qcode{"-all"} 36 ##
40 ## argument may be necessary to find related functions that are not a part of 37 ## Programming Note: The ability of @code{lookfor} to correctly identify the
41 ## Octave. 38 ## first sentence of the help text is dependent on the format of the function's
39 ## help. All Octave core functions are correctly formatted, but the same can
40 ## not be guaranteed for external packages and user-supplied functions.
41 ## Therefore, the use of the @qcode{"-all"} argument may be necessary to find
42 ## related functions that are not a part of Octave.
42 ## @seealso{help, doc, which} 43 ## @seealso{help, doc, which}
43 ## @end deftypefn 44 ## @end deftypefn
44 45
45 function [out_fun, out_help_text] = lookfor (str, arg2) 46 function [fcn, help1str] = lookfor (str, arg2)
46 47
47 if (strcmpi (str, "-all")) 48 if (strcmpi (str, "-all"))
48 ## The difference between using '-all' and not, is which part of the caches 49 ## The difference between using '-all' and not is which part of the caches
49 ## we search. The cache is organized such that the first column contains 50 ## we search. The cache is organized such that row
50 ## the function name, the second column contains the full help text, and 51 ## 1) contains the function name
51 ## the third column contains the first sentence of the help text. 52 ## 2) contains the full help text
53 ## 3) contains the first sentence of the help text.
52 str = arg2; 54 str = arg2;
53 search_type = 2; # when using caches, search the second column 55 search_type = 2; # search the second row (full help text)
54 else 56 else
55 search_type = 3; # when using caches, search the third column 57 search_type = 3; # search the third column (first help sentence)
56 endif 58 endif
57 str = lower (str); # Compare is case insensitive 59 str = lower (str); # Compare is case insensitive
58 60
59 ## Search functions, operators, and keywords that come with Octave 61 ## Search functions, operators, and keywords that come with Octave
60 cache_file = doc_cache_file (); 62 cache_file = doc_cache_file ();
61 if (exist (cache_file, "file")) 63 if (exist (cache_file, "file"))
62 [fun, help_text] = search_cache (str, cache_file, search_type); 64 [fcnlist, help_text] = search_cache (str, cache_file, search_type);
63 had_core_cache = true; 65 had_core_cache = true;
64 else 66 else
65 fun = help_text = {}; 67 fcnlist = help_text = {};
66 had_core_cache = false; 68 had_core_cache = false;
67 endif 69 endif
68 70
69 ## Search functions in new path dirs. 71 ## Search functions in new path dirs.
70 orig_path = ostrsplit (__pathorig__ (), pathsep ()); 72 orig_path = ostrsplit (__pathorig__ (), pathsep ());
71 73
72 ## ditto for path. 74 ## ditto for path.
73 new_path = ostrsplit (path (), pathsep ()); 75 new_path = ostrsplit (path (), pathsep ());
74 76
75 ## scratch out directories already covered by orig_path. 77 ## remove directories already covered by orig_path.
76 if (had_core_cache) 78 if (had_core_cache)
77 new_path = setdiff (new_path, orig_path); 79 new_path = setdiff (new_path, orig_path);
78 endif 80 endif
79 81
80 for n = 1:numel (new_path) 82 for n = 1:numel (new_path)
81 elt = new_path{n}; 83 fcndir = new_path{n};
82 cache_file = fullfile (elt, "doc-cache"); 84 cache_file = fullfile (fcndir, "doc-cache");
83 if (exist (cache_file, "file")) 85 if (exist (cache_file, "file"))
84 ## We have a cache in the directory, then read it and search it! 86 ## We have a cache in the directory, then read it and search it!
85 [funs, hts] = search_cache (str, cache_file, search_type); 87 [fcns, htext] = search_cache (str, cache_file, search_type);
86 fun(end+1:end+length (funs)) = funs; 88 fcnlist(end+1:end+length (fcns)) = fcns;
87 help_text(end+1:end+length (hts)) = hts; 89 help_text(end+1:end+length (htext)) = htext;
90
88 else 91 else
89 ## We don't have a cache. Search files 92 ## We don't have a cache. Search files.
90 funs_in_f = __list_functions__ (elt); 93 for fcn_in_fcndir = (__list_functions__ (fcndir)).'
91 for m = 1:length (funs_in_f) 94 fn = fcn_in_fcndir{1};
92 fn = funs_in_f{m};
93 95
94 ## Skip files that start with __ 96 ## Skip files that start with "__"
95 if (length (fn) > 2 && strcmp (fn(1:2), "__")) 97 if (strncmp (fn, "__", 2))
96 continue; 98 continue;
97 endif 99 endif
100
101 status = 0;
98 102
99 ## Extract first sentence 103 ## Extract first sentence
100 try 104 try
101 warn_state = warning (); 105 warn_state = warning ();
102 unwind_protect 106 unwind_protect
103 warning ("off"); 107 warning ("off");
104 first_sentence = get_first_help_sentence (fn, 1024); 108 first_sentence = get_first_help_sentence (fn, 1024);
105 status = 0;
106 unwind_protect_cleanup 109 unwind_protect_cleanup
107 warning (warn_state); 110 warning (warn_state);
108 end_unwind_protect 111 end_unwind_protect
109 catch 112 catch
110 status = 1; 113 status = 1;
111 end_try_catch 114 end_try_catch
112 115
113 if (search_type == 2) # search entire help text 116 if (status)
117 ## Error getting first help sentence
118 elseif (search_type == 3)
119 ## only search the first sentence of the help text
120 text = first_sentence;
121 elseif (search_type == 2)
122 ## search entire help text
114 try 123 try
115 warn_state = warning (); 124 warn_state = warning ();
116 unwind_protect 125 unwind_protect
117 warning ("off"); 126 warning ("off");
118 [text, fmt] = get_help_text (fn); 127 [text, fmt] = get_help_text (fn);
119 status = 0; 128 switch (lower (fmt))
129 case "plain text"
130 status = 0;
131 case "texinfo"
132 [text, status] = __makeinfo__ (text, "plain text");
133 case "html"
134 [text, status] = strip_html_tags (text);
135 otherwise
136 status = 1;
137 endswitch
120 unwind_protect_cleanup 138 unwind_protect_cleanup
121 warning (warn_state); 139 warning (warn_state);
122 end_unwind_protect 140 end_unwind_protect
123 catch 141 catch
124 status = 1; 142 status = 1;
125 end_try_catch 143 end_try_catch
126
127 ## Take action depending on help text fmt
128 switch (lower (fmt))
129 case "plain text"
130 status = 0;
131 case "texinfo"
132 [text, status] = __makeinfo__ (text, "plain text");
133 case "html"
134 [text, status] = strip_html_tags (text);
135 otherwise
136 status = 1;
137 endswitch
138
139 elseif (status == 0) # only search the first sentence of the help text
140 text = first_sentence;
141 endif 144 endif
142 145
143 ## Search the help text, if we can 146 ## Search the help text
144 if (status == 0 && ! isempty (strfind (lower (text), str))) 147 if (status == 0 && strfind (lower (text), str))
145 fun(end+1) = fn; 148 fcnlist(end+1) = fn;
146 help_text(end+1) = first_sentence; 149 help_text(end+1) = first_sentence;
147 endif 150 endif
148 endfor 151 endfor
149 endif 152 endif
150 endfor 153 endfor
153 ## Print the results (FIXME: it would be nice to break at word boundaries) 156 ## Print the results (FIXME: it would be nice to break at word boundaries)
154 indent = 20; 157 indent = 20;
155 term_width = (terminal_size ())(2); 158 term_width = (terminal_size ())(2);
156 desc_width = term_width - indent - 2; 159 desc_width = term_width - indent - 2;
157 indent_space = blanks (indent); 160 indent_space = blanks (indent);
158 for k = 1:length (fun) 161 for k = 1:length (fcnlist)
159 f = fun{k}; 162 f = fcnlist{k};
160 f(end+1:indent-1) = " "; 163 f(end+1:indent-1) = " ";
161 puts ([f " "]); 164 puts ([f " "]);
162 lf = length (f); 165 lf = length (f);
163 desc = strtrim (strrep (help_text{k}, "\n", " ")); 166 desc = strtrim (strrep (help_text{k}, "\n", " "));
164 ldesc = length (desc); 167 ldesc = length (desc);
166 for start = (desc_width - (lf - indent) + 1):desc_width:ldesc 169 for start = (desc_width - (lf - indent) + 1):desc_width:ldesc
167 stop = min (start + desc_width, ldesc); 170 stop = min (start + desc_width, ldesc);
168 printf ("%s%s\n", indent_space, strtrim (desc (start:stop))); 171 printf ("%s%s\n", indent_space, strtrim (desc (start:stop)));
169 endfor 172 endfor
170 endfor 173 endfor
171
172 else 174 else
173 ## Return the results instead of displaying them 175 ## Return the results instead of displaying them
174 out_fun = fun; 176 fcn = fcnlist;
175 out_help_text = help_text; 177 help1str = help_text;
176 endif 178 endif
177 179
178 endfunction 180 endfunction
179 181
180 function [funs, help_texts] = search_cache (str, cache_file, search_type) 182 function [fcns, help_texts] = search_cache (str, cache_file, search_type)
181 load (cache_file); 183 load (cache_file);
182 if (! isempty (cache)) 184 if (! isempty (cache))
183 t1 = strfind (lower (cache (1, :)), str); 185 t1 = strfind (lower (cache (1, :)), str);
184 t2 = strfind (lower (cache (search_type, :)), str); 186 t2 = strfind (lower (cache (search_type, :)), str);
185 cache_idx = find (! (cellfun ("isempty", t1) & cellfun ("isempty", t2))); 187 cache_idx = find (! (cellfun ("isempty", t1) & cellfun ("isempty", t2)));
186 funs = cache(1, cache_idx); 188 fcns = cache(1, cache_idx);
187 help_texts = cache(3, cache_idx); 189 help_texts = cache(3, cache_idx);
188 else 190 else
189 funs = help_texts = {}; 191 fcns = help_texts = {};
190 endif 192 endif
191 endfunction 193 endfunction
192 194