Remixed!
-Jonathan
--- On Thu, 1/13/11, Hans-Christoph Steiner <[email protected]> wrote:
> From: Hans-Christoph Steiner <[email protected]>
> Subject: Re: [PD] keyword/regexp search of documentation in a plugin
> To: "Jonathan Wilkes" <[email protected]>
> Cc: "pd-list" <[email protected]>
> Date: Thursday, January 13, 2011, 6:00 PM
> Attached is an updated version:
>
>
> On Jan 12, 2011, at 9:13 PM, Jonathan Wilkes wrote:
>
> > 1 the results aren't clickable
>
> Which platform? They are for me on Ubuntu/maverick,
> Mac OS X 10.5 and 10.6.
>
> > 2 you can't enter multiple non-contiguous terms
>
> Its a regexp really, so it doesn't really do keyword
> searches. Ideally, this would use a search engine like
> xapian, then it could do keyword searches. I just
> added code to replace spaces in the searchtext with the
> regexp code ".*" so that it'll search non-contiguous words,
> but the first word will always be before the second in
> search results.
>
> > 3 no control over AND vs. OR (or is there?)
>
> regexp
>
> > 4 doesn't differentiate between tutorial/example
> patches and object-help
> > patches (what if I just want to find the object named
> 'gate'?)
>
> Hmm, that wouldn't be too hard to do, I guess it would be a
> pull down menu of: object, message, comment, array, any.
>
> > 5 most of the results don't fit into the window size
>
> The window should be resizable.
>
> > 6 full text search makes it impossible to get useful
> results for 'float',
> > array', 'list', etc.
>
> That sounds like fully typed searching, which would be very
> nice, but much harder to do. My goal right now is to
> get a basic search function working. Hopefully my code
> is clear enough that others will make their own custom
> search plugins. I could see simple search, regexp,
> search engine, etc.
>
> > 7 can't search by inlet, object function, author, etc.
> (PDDP META tags)
>
> Why not? This works for me: author.*steiner
>
> > 8 non-friendly user interface
>
> I spruced it up a bit with this latest version.
>
> > 9 it doesn't seem to be searching the manual
>
> Ah, I'll add .html to the file types it searches.
>
> .hc
>
> >
> > I've already got a pd patch that is well on its way to
> curing 1-8 (posted
> > screenshots awhile back), but it requires toxy, which
> seems to have been
> > removed from pd-ext, and there is currently no
> (non-buggy) tk 'entry'
> > object in existence.
> >
> > -Jonathan
> >
> >
> > --- On Wed, 1/12/11, Hans-Christoph Steiner <[email protected]>
> wrote:
> >
> >> From: Hans-Christoph Steiner <[email protected]>
> >> Subject: [PD] keyword/regexp search of
> documentation in a plugin
> >> To: "pd-list" <[email protected]>
> >> Date: Wednesday, January 12, 2011, 7:10 AM
> >>
> >> Hey all,
> >>
> >> At the strong urging of Sofy Yuditskaya, I finally
> wrote up
> >> a quick
> >> interface for searching the Pd docs using a
> keyword or a
> >> regexp. Its in
> >> the form of an 0.43 plugin, so you can just drop
> it into
> >> your
> >> user-folder and you should get a "Search" item on
> the Help
> >> menu.
> >>
> >> Test it out and let me know how it works for you.
> >>
> >> .hc
> >>
> >>
> >> -----Inline Attachment Follows-----
> >>
> >> _______________________________________________
> >> [email protected]
> >> mailing list
> >> UNSUBSCRIBE and account-management ->
> >> http://lists.puredata.info/listinfo/pd-list
> >>
> >
> >
> >
>
>
> ----------------------------------------------------------------------------
>
> “We must become the change we want to see. - Mahatma
> Gandhi
>
>
# plugin to allow searching all the documentation using a regexp
# check the Help menu for the Search item to use it
package require Tk 8.5
package require pd_bindings
package require pd_menucommands
namespace eval ::dialog_search:: {
variable searchtext {}
variable count {}
variable search_history {}
variable history_position 0
variable object_state {1}
variable all_about_state {1}
variable tutorial_state {1}
variable other_state {1}
}
proc ::dialog_search::get_history {direction textwidget} {
variable search_history
variable history_position
incr history_position $direction
if {$history_position < 0} {set history_position 0}
if {$history_position > [llength $search_history]} {
set history_position [llength $search_history]
}
$textwidget delete 0 end
$textwidget insert 0 [lindex $search_history end-[expr $history_position - 1]]
$textwidget selection range 0 end
}
# TODO search type pulldown menu: object, message, comment, array, any
# TODO search filenames also
# TODO check line formatting options
# find_doc_files
# basedir - the directory to start looking in
proc ::dialog_search::find_doc_files { basedir } {
# Fix the directory name, this ensures the directory name is in the
# native format for the platform and contains a final directory seperator
set basedir [string trimright [file join $basedir { }]]
set fileList {}
# Look in the current directory for matching files, -type {f r}
# means ony readable normal files are looked at, -nocomplain stops
# an error being thrown if the returned list is empty
foreach fileName [glob -nocomplain -type {f r} -path $basedir $helpbrowser::doctypes] {
lappend fileList $fileName
}
# Now look for any sub direcories in the current directory
foreach dirName [glob -nocomplain -type {d r} -path $basedir *] {
# Recusively call the routine on the sub directory and append any
# new files to the results
set subDirList [find_doc_files $dirName]
if { [llength $subDirList] > 0 } {
foreach subDirFile $subDirList {
lappend fileList $subDirFile
}
}
}
return $fileList
}
proc ::dialog_search::open_file { xpos ypos textwidget } {
set i [$textwidget index @$xpos,$ypos]
set range [$textwidget tag prevrange filename $i]
set filename [eval $textwidget get $range]
set range [$textwidget tag nextrange basedir $i]
set basedir [eval $textwidget get $range]
pdtk_post "Basedir is $basedir and file is $filename\n"
append basedir "/"
if {$filename ne ""} {
menu_doc_open $basedir $filename
}
}
# only does keywords for now-- should make it handle any meta tags
proc ::dialog_search::grab_metavalue { xpos ypos textwidget } {
# offset y to correct for tag indention-- not sure why it has to be so
# large...
set xpos_offset 20
set xpos [expr {$xpos + $xpos_offset}]
set i [$textwidget index @$xpos,$ypos]
set range [$textwidget tag prevrange metavalue $i]
set value [eval $textwidget get $range]
set text {keywords.*}
append text $value
set ::dialog_search::searchtext ""
set ::dialog_search::searchtext $text
search
}
proc ::dialog_search::showhide { state } {
if { $state eq "1" } {
return {off}
} else {
return {on}
}
}
# show/hide results based on genre
proc ::dialog_search::filter_results { textwidget } {
variable all_about_state
variable object_state
variable tutorial_state
variable other_state
$textwidget tag configure objectclass -background "#c4dcdc" \
-elide [::dialog_search::showhide $object_state]
$textwidget tag configure all_about_pd -background "#fcbcc4" \
-elide [::dialog_search::showhide $all_about_state]
$textwidget tag configure tutorial -background "#fcc048" \
-elide [::dialog_search::showhide $tutorial_state]
$textwidget tag configure other -background "#ffffff" \
-elide [::dialog_search::showhide $other_state]
}
proc ::dialog_search::readfile {filename} {
set fp [open $filename]
set file_contents [split [read $fp] \n]
close $fp
return $file_contents
}
proc ::dialog_search::search { } {
variable searchtext
variable search_history
if {$searchtext eq ""} return
if { [lsearch $search_history $searchtext] eq "-1" } {
lappend search_history $searchtext
.search.searchtextentry configure -values $search_history
}
pdtk_post "Appended $searchtext to history\n"
.search.searchtextentry selection clear
.search.searchtextentry configure -foreground gray -background gray90
.search.resultstext configure -state normal
.search.resultstext delete 0.0 end
update idletasks
do_search
# BUG? the above might cause weird bugs, so consider http://wiki.tcl.tk/1255
# and http://wiki.tcl.tk/1526
# after idle [list after 10 ::dialog_search::do_search]
}
proc ::dialog_search::do_search { } {
variable searchtext
variable count
set count 0
set widget .search.resultstext
foreach basedir [concat [file join $::sys_libdir doc] $::sys_searchpath $::sys_staticpath] {
# Fix the directory name, this ensures the directory name is in the
# native format for the platform and contains a final directory seperator
set basedir [file normalize $basedir]
foreach docfile [find_doc_files $basedir] {
searchfile $searchtext [readfile $docfile] $widget \
[string replace $docfile 0 [string length $basedir]] $basedir
}
}
.search.searchtextentry configure -foreground black -background white
$widget insert 0.0 " Found $count matching docs.\n"
$widget insert 0.0 "Home" "link intro"
$widget configure -state disabled
}
proc ::dialog_search::searchfile {searchtext file_contents widget filename basedir} {
variable count
set match 0
set description ""
set keywords ""
set matchingtext ""
set genre ""
set metadata ""
# set searchtext [regsub -all { } $searchtext {.*}]
if {[regexp -nocase -- ".*-help.pd" $filename]} {
set genre "objectclass"
}
if {[regexp -nocase -- "\[^a-zA-Z\]$searchtext" $filename]} {
set match [llength $searchtext]
} else {
set terms [split $searchtext]
foreach term $terms {
foreach line $file_contents {
if {[regexp -nocase -- "$term" $line]} {
incr match
break
}
}
}
}
if { $match eq [llength [split $searchtext]] } {
set len [llength [split $searchtext]]
pdtk_post "Length is $len. Searchtext is $searchtext.\n"
incr count
regexp -nocase -- "#X text \[0-9\]+ \[0-9\]+ description\[:\]? (.*?);.*" $file_contents -> description
regsub -all {[{}]} $description {} description
regexp -nocase -- "#X text \[0-9\]+ \[0-9\]+ keywords\[:\]? (.*?);.*" $file_contents -> keywords
regsub -all {[{}]} $keywords {} keywords
if {[regexp -nocase -- "#X text \[0-9\]+ \[0-9\]+ genre all_about_pd" $file_contents]} {
set genre "all_about_pd"
}
if {[regexp -nocase -- "#X text \[0-9\]+ \[0-9\]+ genre tutorial" $file_contents]} {
set genre "tutorial"
}
}
# The following would print out the line of the matched text, but I found it
# ugly and rather unhelpful:
# foreach line $file_contents {
# TODO this could be optimized so that the lines are added to
# a var, then the regsubs are run on the whole text, then its
# inserted into the widget
# if { $match ne "1" } {
# if {[regexp -nocase -- "\[^a-zA-Z\]$searchtext" $line]} {
# set line [regsub { \\} $line {}]
# set line [regsub {\\} $line {}]
# set line [regsub {^#X text [0-9]+ [0-9]+ (.*?);*} $line {\1}]
# set line [regsub {^#X obj [0-9]+ [0-9]+ (.*?);*} $line {ï¼» \1ï¼½}]
# set line [regsub {^#X msg [0-9]+ [0-9]+ (.*?);*} $line {â \1ã}]
# set line [regsub {^#X (\S+) [0-9]+ [0-9]+(.*?);*} $line {ã\1ã\2}]
# set matchingtext [regsub {^#N \S+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ (.*?);} \
# $line {[pd \1]}]
# set match 1
# }
# }
# }
if { $genre eq "" } {
set genre "other"
}
if { $match eq [llength [split $searchtext]] } {
$widget insert end "$filename" "filename link $genre"
$widget insert end "$basedir" basedir
if { $description eq "" } {
set description "No DESCRIPTION tag."
}
$widget insert end "\n$description\n" "description $genre"
if { $keywords ne "" } {
$widget insert end "Keywords:" "keywords $genre"
foreach value $keywords {
$widget insert end " " $genre
$widget insert end $value "metavalue keywords link $genre"
}
$widget insert end "\n" $genre
}
}
}
proc ::dialog_search::ok {mytoplevel} {
# this is a placeholder for the standard dialog bindings
}
proc ::dialog_search::cancel {mytoplevel} {
wm withdraw .search
}
proc ::dialog_search::open_search_dialog {mytoplevel} {
if {[winfo exists $mytoplevel]} {
wm deiconify $mytoplevel
raise $mytoplevel
} else {
create_dialog $mytoplevel
}
}
proc ::dialog_search::sa { widget } {
$widget selection range 0 end
break
}
proc ::dialog_search::intro { t } {
$t configure -state normal
$t delete 0.0 end
$t insert end "Pure Data Documentation Search\n"
$t insert end "Enter search terms above or use one of the "
$t insert end "tags below to find help in the form of object "
$t insert end "class help patches, reference patches all about "
$t insert end "Pure Data concepts, tutorials, and everything "
$t insert end "else.\n"
$t insert end "Tags\n"
$t insert end "abstraction" "metavalue link keywords"
$t insert end " object itself is written in Pure Data\n" def
$t insert end "abstraction_op" "metavalue link keywords"
$t insert end " object that only makes sense in terms of" def
$t insert end "abstractions\n" def
$t insert end "analysis" "metavalue link keywords"
$t insert end " object that does analysis\n" def
$t insert end "anything_op" "metavalue link keywords"
$t insert end " store or manipulate an anything\n" def
$t insert end "array" "metavalue link keywords"
$t insert end " objects for creating and editing arrays\n" def
$t insert end "bandlimited" "metavalue link keywords"
$t insert end " objects that describe themselves as being" def
$t insert end "bandlimited\n" def
$t insert end "block_oriented" "metavalue link keywords"
$t insert end " see Matju's definition\n" def
$t insert end "canvas_op" "metavalue link keywords"
$t insert end " object whose behavior only makes sense in terms " def
$t insert end "of a canvas\n" def
$t insert end "control" "metavalue link keywords"
$t insert end " control rate objects\n" def
$t insert end "conversion" "metavalue link keywords"
$t insert end " convert from one set of units to another\n" def
$t insert end "data_structure" "metavalue link keywords"
$t insert end " objects for creating and managing data structures\n" def
$t insert end "filter" "metavalue link keywords"
$t insert end " object that filters the data\n" def
$t insert end "GUI" "metavalue link keywords"
$t insert end " objects that provide a graphical user interface\n" def
$t insert end "list_op" "metavalue link keywords"
$t insert end " object that manipulates or stores a list\n" def
$t insert end "MIDI" "metavalue link keywords"
$t insert end " objects that provide MIDI functionality\n" def
$t insert end "needs_work" "metavalue link keywords"
$t insert end " help patches under construction\n" def
$t insert end "network" "metavalue link keywords"
$t insert end " object that provides access to or sends/receives " def
$t insert end "data over a network connection.\n" def
$t insert end "nonlocal" "metavalue link keywords"
$t insert end " objects that can make nonlocal connections to " def
$t insert end "other objects (i.e., communicate with other objects " def
$t insert end "without wires\n" def
$t insert end "orphan" "metavalue link keywords"
$t insert end " help patches that can't get accessed by right-clicking" def
$t insert end {on the corresponding object (like [drawsymbol])} def
$t insert end "\n" def
$t insert end "patchfile_op" "metavalue link keywords"
$t insert end " object whose behavior only makes sense in " def
$t insert end "terms of a patchfile\n" def
$t insert end "pd_op" "metavalue link keywords"
$t insert end " object that can report on or manipulate global Pd " def
$t insert end "operation\n" def
$t insert end "ramp" "metavalue link keywords"
$t insert end " a ramp\n" def
$t insert end "random" "metavalue link keywords"
$t insert end " object outputs a random value, list, or signal\n" def
$t insert end "signal" "metavalue link keywords"
$t insert end " audiorate objects\n" def
$t insert end "soundfile" "metavalue link keywords"
$t insert end " object that can play, manipulate, and/or save a " def
$t insert end "sound file (wav, ogg, mp3, etc.)\n" def
$t insert end "storage" "metavalue link keywords"
$t insert end " objects whose main function is to store a value\n" def
$t insert end "symbol_op" "metavalue link keywords"
$t insert end " object that manipulates or stores a symbol\n" def
$t insert end "time" "metavalue link keywords"
$t insert end " objects that measure time or which the user can " def
$t insert end "use to manipulate time\n" def
$t insert end "trigonometry" "metavalue link keywords"
$t insert end " objects that provide trigonometric functionality\n" def
}
proc ::dialog_search::create_dialog {mytoplevel} {
variable selected_file
toplevel $mytoplevel
wm title $mytoplevel [_ "Search"]
ttk::combobox $mytoplevel.searchtextentry -textvar ::dialog_search::searchtext \
-font "Helvetica 12"
$mytoplevel.searchtextentry insert 0 "Enter search terms"
$mytoplevel.searchtextentry selection range 0 end
# entry $mytoplevel.searchtextentry -bg white -textvar ::dialog_search::searchtext \
# -highlightbackground lightgray \
# -highlightcolor blue -font 18 -borderwidth 1
bind $mytoplevel.searchtextentry <Return> "$mytoplevel.searchbutton invoke"
# bind $mytoplevel.searchtextentry <Up> \
# "::dialog_search::get_history 1 $mytoplevel.searchtextentry"
# bind $mytoplevel.searchtextentry <Down> \
# "::dialog_search::get_history -1 $mytoplevel.searchtextentry"
bind $mytoplevel.searchtextentry <$::modifier-Key-a> \
"$mytoplevel.searchtextentry selection range 0 end; break"
text $mytoplevel.resultstext -yscrollcommand "$mytoplevel.yscrollbar set" \
-bg white -highlightcolor blue -height 30 -width 60 -wrap word -state disabled
$mytoplevel.resultstext tag configure filename -foreground "#0000ff" -underline on \
-font "helvetica 14" -lmargin1 5 -lmargin2 5 -spacing1 5
$mytoplevel.resultstext tag configure metavalue -foreground "#0000ff"
$mytoplevel.resultstext tag configure basedir -elide on
$mytoplevel.resultstext tag configure description -font "helvetica 12" \
-lmargin1 5 -lmargin2 5
$mytoplevel.resultstext tag configure keywords \
-lmargin1 5 -lmargin2 5 -font "helvetica 12"
$mytoplevel.resultstext tag configure def \
-lmargin1 5 -lmargin2 5 -font "helvetica 10"
scrollbar $mytoplevel.yscrollbar -command "$mytoplevel.resultstext yview" \
-takefocus 0
button $mytoplevel.searchbutton -text [_ "Search"] -takefocus 0 \
-background lightgray -highlightbackground lightgray \
-command ::dialog_search::search
# Genre tags
$mytoplevel.resultstext tag configure all_about_pd -background "#fcbcc4"
$mytoplevel.resultstext tag configure objectclass -background "#c4dcdc"
$mytoplevel.resultstext tag configure tutorial -background "#fcc048"
$mytoplevel.resultstext tag configure other
ttk::frame $mytoplevel.f -width 80
# ttk::style configure obj.TCheckbutton -background "#c4dcdc"
# ttk::style configure aapd.TCheckbutton -background "#fcbcc4"
# ttk::style configure tutorial.TCheckbutton -background "#fcc048"
# ttk::style configure other.TCheckbutton -background "#ffffff"
set blah raised
checkbutton $mytoplevel.f.objectcheck -text "Object Classes" -background "#c4dcdc" \
-activebackground "#c4dcdc" -variable ::dialog_search::object_state \
-command "::dialog_search::filter_results $mytoplevel.resultstext" -borderwidth 1 -relief $blah
checkbutton $mytoplevel.f.all_aboutcheck -text "All About Pd" -background "#fcbcc4" \
-activebackground "#fcbcc4" -variable ::dialog_search::all_about_state \
-command "::dialog_search::filter_results $mytoplevel.resultstext" -borderwidth 1 -relief $blah
checkbutton $mytoplevel.f.tutorialcheck -text "Tutorials " -background "#fcc048" \
-activebackground "#fcc048" -variable ::dialog_search::tutorial_state \
-command "::dialog_search::filter_results $mytoplevel.resultstext" -borderwidth 1 -relief $blah
checkbutton $mytoplevel.f.othercheck -text "Other " -background "#ffffff" \
-activebackground "#ffffff" -variable ::dialog_search::other_state \
-command "::dialog_search::filter_results $mytoplevel.resultstext" -borderwidth 1 -relief $blah
grid $mytoplevel.f.objectcheck $mytoplevel.f.all_aboutcheck $mytoplevel.f.tutorialcheck \
$mytoplevel.f.othercheck
label $mytoplevel.advancedlabel -text "Advanced" -foreground "#0000ff"
bind $mytoplevel.advancedlabel <Enter> "$mytoplevel.advancedlabel configure \
-cursor hand2 "
bind $mytoplevel.advancedlabel <Leave> "$mytoplevel.advancedlabel configure \
-cursor xterm "
bind $mytoplevel.advancedlabel <Button-1> \
{menu_doc_open doc/5.reference all_about_finding_objects.pd}
grid $mytoplevel.searchtextentry -column 0 -row 0 -sticky ew
grid $mytoplevel.searchbutton -column 1 -columnspan 2 -row 0 -sticky ew
grid $mytoplevel.f -column 0 -row 1 -sticky ew
grid $mytoplevel.advancedlabel -column 1 -columnspan 2 -row 1 -sticky e
grid $mytoplevel.resultstext -column 0 -columnspan 2 -row 2 -sticky news
grid $mytoplevel.yscrollbar -column 2 -row 2 -sticky nes
grid columnconfigure $mytoplevel 0 -weight 1
grid rowconfigure $mytoplevel 2 -weight 1
$mytoplevel.resultstext tag configure link -foreground "#0000ff"
$mytoplevel.resultstext tag bind intro <Button-1> " ::dialog_search::intro \
$mytoplevel.resultstext "
$mytoplevel.resultstext tag bind metavalue <Button-1> " ::dialog_search::grab_metavalue %x %y \
$mytoplevel.resultstext "
$mytoplevel.resultstext tag bind filename <Button-1> " ::dialog_search::open_file %x %y \
$mytoplevel.resultstext "
$mytoplevel.resultstext tag bind link <Enter> " $mytoplevel.resultstext configure \
-cursor hand2 "
$mytoplevel.resultstext tag bind link <Leave> " $mytoplevel.resultstext configure \
-cursor xterm "
::pd_bindings::dialog_bindings $mytoplevel "search"
focus $mytoplevel.searchtextentry
::dialog_search::intro $mytoplevel.resultstext
}
# create the menu item on load
set mymenu .menubar.help
set inserthere [$mymenu index [_ "Report a bug"]]
$mymenu insert $inserthere separator
$mymenu insert $inserthere command -label [_ "Search"] \
-command {::dialog_search::open_search_dialog .search}
_______________________________________________
[email protected] mailing list
UNSUBSCRIBE and account-management ->
http://lists.puredata.info/listinfo/pd-list