home *** CD-ROM | disk | FTP | other *** search
Text File | 1999-04-22 | 55.0 KB | 1,805 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "bibtexMode.tcl"
- # created: 17/8/94 {9:12:06 am}
- # last update: 22/4/1999 {6:10:19 pm}
- # Updated by: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Engineering and Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # Major rewrite of most of BibTeX mode. Original by Tom Pollard.
- # See the end of the BibTeX Help file for a history.
- #
- # ###################################################################
- ##
-
- alpha::mode Bib 3.4 bibtexMenu {*.bib *.inspec *.bst *.hollis *.isi} {
- texMenu bibtexMenu electricReturn electricTab
- } {
- addMenu bibtexMenu "•282" Bib
- alpha::package require -loose AlphaTcl 7.1.8
- } uninstall {this-file} help {file "BibTeX Help"}
- # to make sure tex-mode is loaded
- texMenu
- # Since we use the TeX menu
- hook::register activateHook makeProcessMenu Bib
-
- newPref v bibAutoIndex 1 Bib "" [list "Never make index" \
- "Ask user when it is necessary" "Always remake when necessary"] index
-
- newPref v suffixString { \\\\} Bib
- newPref v prefixString {% } Bib
- newPref v fillColumn {65} Bib
- newPref f wordWrap {1} Bib
- newPref f autoMark {1} Bib
-
- ##
- # The first is for to look in the Tex-inputs folder and, by default, is
- # off (easily you can put it on!). The two others permit you to use the
- # new feature of Alpha that associates any mode with folders to look into
- # for files.... These folders are managed in the config menu. The last,
- # of course is to permit the current file folder to be a look for
- # bibliography folder. Sometimes you want the third possibility, others
- # the two last, others all of them.
- ##
- newPref f useTexPaths {0} Bib
- newPref f useModePaths {1} Bib
- newPref f useCurrentPath {1} Bib
-
- ###########################################################################
- # Search patterns for entries and cite-keys
- #
- # set bibTopPat {^[ ]*@[a-zA-Z]+[\{\(]([-A-Za-z0-9_:/\.]+)}
- # match entry type
- set bibTopPat {^[ ]*@([a-zA-Z]+)[\{\(]}
- # match cite-key
- set bibTopPat1 {^[ ]*@[a-zA-Z]+[\{\(][ ]*([^=, ]+)}
- # match type and cite-key
- set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
- # match first field (no cite-key)
- set bibTopPat3 {^[ ]*@([a-zA-Z]+)[\{\(]([ ]*[a-zA-Z]+[ ]*=[ ]*)}
-
- newPref v wordBreak {[a-zA-Z0-9]+} Bib
- newPref v wordBreakPreface {[^a-zA-Z0-9]} Bib
- newPref v funcExpr $bibTopPat Bib
-
- newPref f overwriteBuffer {1} Bib
- newPref f fieldBraces {1} Bib
- newPref f entryBraces {1} Bib
- newPref f segregateStrings {1} Bib
- newPref f markStrings {0} Bib
- newPref f alignEquals {0} Bib
- newPref f zapEmptyFields {0} Bib
- newPref f descendingYears {1} Bib
- newPref v indentString { } Bib
- newPref v stdAbbrevs {jan feb mar apr may jun jul aug sep oct nov dec} Bib
-
- # ◊◊◊◊ Option-click title bar ◊◊◊◊ #
- # use TeX routines for Bib mode
- proc Bib::OptionTitlebar {} {TeX::OptionTitlebar}
- proc Bib::OptionTitlebarSelect {item} {TeX::OptionTitlebarSelect $item}
-
- ###########################################################################
- # BibTeX Key Bindings.
- ###########################################################################
- # abbreviations: <o> = option, <z> = control, <s> = shift, <c> = command
- #
- Bind 'b' <sz> selectEntry "Bib"
- Bind 'n' <sz> nextEntry "Bib"
- Bind 'p' <sz> prevEntry "Bib"
-
- Bind 'f' <sz> searchFields "Bib"
- Bind 'm' <sz> searchEntries "Bib"
- Bind 'l' <sz> formatEntry "Bib"
-
- ###########################################################################
- # Data Definitions
- ###########################################################################
- ###########################################################################
- # Define the data arrays that contain the names of the required,
- # optional, and preferred fields for each entry type.
- #
- # The index names of the rqdFld() array _define_ the valid entry types
- # recognized by the program.
- #
- set rqdFld(article) {author title journal year}
- set optFld(article) {volume number pages month note}
- # example of how to assign your own preferences to some items
- #set myFld(article) {author title journal volume pages year note}
-
- set rqdFld(book) {author title publisher year}
- set optFld(book) {editor volume number series address edition month note}
-
- set rqdFld(booklet) {title}
- set optFld(booklet) {author howpublished address month year note}
-
- set rqdFld(conference) {author title booktitle year}
- set optFld(conference) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(inBook) {author title chapter publisher year}
- set optFld(inBook) {editor pages volume number series address edition month type note}
-
- set rqdFld(inCollection) {author title booktitle publisher year}
- set optFld(inCollection) {editor volume number series type chapter pages address edition month note}
-
- set rqdFld(inProceedings) {author title booktitle year}
- set optFld(inProceedings) {editor volume number series pages organization publisher address month note}
-
- set rqdFld(manual) {title}
- set optFld(manual) {author organization address edition year month note}
-
- set rqdFld(mastersThesis) {author title school year}
- set optFld(mastersThesis) {address month note type}
-
- set rqdFld(misc) {}
- set optFld(misc) {author title howpublished year month note}
-
- set rqdFld(phdThesis) {author title school year}
- set optFld(phdThesis) {address month type note}
-
- set rqdFld(proceedings) {title year}
- set optFld(proceedings) {editor volume number series publisher organization address month note}
-
- set rqdFld(techReport) {author title institution year}
- set optFld(techReport) {type number address month note}
-
- set rqdFld(unpublished) {author title note}
- set optFld(unpublished) {year month}
-
- set entryNames [lsort [array names rqdFld]]
- set customEntries [lsort [array names myFld]]
-
- ###########################################################################
- # Define an array of flags indicating whether the data a given field
- # type should be quoted. The actual characters used to quote the field are
- # given by $bibOpenQuote and $bibCloseQuote, which are set by the routine
- # 'bibFieldDelims' according to the flag $fieldBraces.
- #
- # Note that the index names of the useBrace() array _define_ the valid
- # field types recognized by the program.
- #
- array set useBrace {
- address 1 annote 1 author 1 booktitle 1 chapter 0 crossref 1 edition 1
- editor 1 howpublished 1 institution 1 journal 1 key 1 language 1 month
- 1 note 1 number 0 organization 1 pages 1 publisher 1 school 1 series 1
- title 1 type 1 volume 0 year 0 isbn 1 customField 1 city 1
- }
-
- set fieldNames [lsort [array names useBrace]]
- ###########################################################################
- # Default values for newly created fields
- #
- set defFldVal(language) "german"
-
- set fieldDefs [lsort [array names defFldVal]]
-
- ###########################################################################
- # BibTeX-mode mode definition
- ###########################################################################
-
- set bibtexKeyWords $fieldNames
- regModeKeywords -e {%} -m {@} -c red -k blue Bib $bibtexKeyWords
- unset bibtexKeyWords
-
- ###########################################################################
- # BibTeX Menu Definition.
- ###########################################################################
- proc bibtexMenu {} {}
-
- proc bibtex {} {
- global bibtexSig
- set name [app::launchAnyOfThese {BIBt Vbib CMTu} bibtexSig]
- switchTo [file tail $name]
- }
-
- menu::buildProc bibtexMenu Bib::buildBibMenu
-
- proc Bib::buildBibMenu {} {
- global bibtexMenu
- return [list "build" \
- [list "/-<U<Obibtex" "(-)" \
- [list Menu -n Entries -p makeEntry {}] \
- [list Menu -n Fields -p makeField {}] \
- "(-)" \
- "/B<U<BselectEntry" "/N<U<BnextEntry" "/P<U<BprevEntry" \
- "/L<U<BformatEntry" "/C<U<BcopyCiteKey" \
- "(-)" \
- "/M<U<BsearchEntries" "/F<U<BsearchFields" \
- {Menu -n sortBy... -p bibSortProc {
- "citeKey"
- "firstAuthor,Year"
- "lastAuthor,Year"
- "year,FirstAuthor"
- "year,LastAuthor"}
- } \
- {Menu -n sortMarks... -p markSortProc {
- "alphabetically"
- "byPosition"}
- } \
- "(-)" \
- "countEntries" "formatAllEntries" \
- "/Q<IquickFindCitation" \
- "/A<U<BaddWinToDatabase" \
- "/I<U<IindexOfThisWindow" \
- "(-)" \
- "rebuildIndex" \
- "rebuildDatabase"] \
- Bib::menuProc \
- [list Entries Fields] \
- $bibtexMenu]
- }
-
- proc Bib::menuProc {menu item} {
- menu::generalProc Bib $item 0
- }
-
- proc Bib::quickFindCitation {} {
- Bib::GotoEntry [prompt::statusLineComplete "Citation" Bib::completionsForEntry \
- -preeval {source [file join $PREFS bibIndex]} -posteval {unset bibIndex}]
- }
-
- proc Bib::completionsForEntry {pref} {
- Bib::_FindAllEntries $pref 0
- }
- set menu::items(Entries) [concat $entryNames "(-)" "customEntry"]
- set menu::proc(Entries) makeEntry
- set menu::items(Fields) [concat $fieldNames "(-)" "customField" "multipleFields"]
- set menu::proc(Fields) makeField
-
- menu::buildSome bibtexMenu
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::openFile" --
- #
- # Given a filename, and the directory of the base '.aux' file, try and
- # find the file. If we don't succeed, pass the request onto the TeX
- # code.
- # -------------------------------------------------------------------------
- ##
- proc Bib::openFile {filename {dir ""}} {
- # look where base file was
- if {![catch {file::openQuietly [file join ${dir} ${filename}]}]} {
- return
- }
- # look in bibtex inputs folder
- global bibtexSig
- if {![catch {file::openQuietly [file join [file dirname [nameFromAppl $bibtexSig]] "BibTeX inputs" ${filename}]}]} {
- return
- }
- # look in all usual tex places
- openTeXFile "$filename"
- return
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::noEntryExists" --
- #
- # No entry exists in the known .bib files. Either add an entry, possibly
- # in a new bibliography file, or add a .bib file to those currently
- # searched.
- # -------------------------------------------------------------------------
- ##
- proc Bib::noEntryExists {item {basefile ""}} {
- set basefile [Bib::getBasefile $basefile]
- set choice [dialog::optionMenu \
- "No entry '$item' exists. What do you want to do?" \
- [list "New entry" "New entry in new bibliography file" \
- "Add .bib file to \\bibliography\{…\}" \
- "Change original citation" \
- "Search all bibliographies" ]]
- switch -- $choice {
- "New entry" {
- Bib::_newEntry $item
- }
- "New entry in new bibliography file" {
- Bib::_newEntry $item 1
- }
- "Add .bib file to \\bibliography\{…\}" {
- Bib::insertNewBibliography $basefile
- }
- "Search all bibliographies" {
- alertnote "Not yet implemented"
- }
- "Change original citation" {
- Bib::changeOriginalCitation $item $basefile
- }
- "Cancel" {
- # nothing
- }
- }
- }
-
- proc Bib::_newEntry {item {new_file 0}} {
- if {$new_file} {
- set bibfile [putfile "Save new bibliography as…" ".bib"]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } else {
- new -n $bibfile
- }
- } else {
- # need to pick a .bib file
- set bibfile [Bib::pickBibliography 1 \
- "Select a bibliography file to which to add an entry"]
- openTeXFile $bibfile
- }
- global entryNames
- bibFormatSetup
- newEntry [listpick -p "Which type of entry?" $entryNames]
- insertText $item
- ring::+
-
- }
-
- proc Bib::changeOriginalCitation {citation {basefile ""}} {
- if {$basefile == ""} {set basefile [TeX_currentBaseFile]}
- # find .aux and open base .tex/.ltx
- if {[set proj [isWindowInFileset $basefile "tex"]] != ""} {
- set files [texListFilesInFileSet $proj]
- } else {
- set files $basefile
- }
- set got "[eval grep [list $citation] $files]\r"
- if {[string first "; Line " $got] == [string last "; Line " $got]} {
- # just one match
- if ![regexp {∞([^\r\n]*)[\r\n]} $got dmy filename] {
- alertnote "I couldn't find the original. You probably have a\
- multi-part document which you haven't made into a TeX fileset.\
- Unless it's a fileset, I can't find the other files."
- return
- }
- file::openQuietly $filename
- eval select [searchInFile $filename $citation 1]
- message "This is the original citation. Change it, then re-run LaTeX and BibTeX."
- } else {
- grepsToWindow "* List of citations *" $got
- }
- }
-
- proc Bib::getBasefile {{basefile ""}} {
- if {$basefile == ""} {return [TeX_currentBaseFile]}
- # find .aux and open base .tex/.ltx
- set base [file root $basefile]
- if [file exists ${base}.tex] {
- return ${base}.tex
- } elseif [file exists ${base}.ltx] {
- return ${base}.ltx
- } else {
- alertnote "Base file with name '${base}.tex/ltx' not found."
- error ""
- }
- }
-
- proc Bib::insertNewBibliography {{basefile ""} {bibfile ""}} {
- set basefile [Bib::getBasefile $basefile]
- file::openQuietly ${basefile}
-
- # find bibliography, position cursor and add
- pushPosition
- endOfBuffer
- if {[catch {set pos [search -s -f 0 -r 0 -m 0 "\\bibliography\{" [getPos]]}]} {
- # add the environment
- set pos [search -s -f 0 "\\end\{document\}" [getPos]]
- goto [pos::math [minPos] + [lindex $pos 0]]
- set preinsert "\\bibliography\{"
- set postinsert "\}\r\r"
- } else {
- set preinsert ""
- set postinsert ","
- goto [pos::math [minPos] + [lindex $pos 1]]
- }
- if {$bibfile == ""} {
- set bibfile [Bib::pickBibliography 0 \
- "Select a bibliography file to add"]
- }
- insertText "${preinsert}[lindex [split $bibfile "."] 0]${postinsert}"
- message "press <Ctrl .> to return to original cursor position"
- }
-
- # Used by Bib::pickBibliography to set a default in the listpick dialog
- # It's useful because you will often want to add a bunch of new items
- # in a row to the same bibliography.
- # NOTE: this is set by my code, not you.
- set Bib::_defaultBib ""
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::pickBibliography" --
- #
- # Put up a list-dialog so the user can select a bibliography file for
- # some action (taken by the caller). Can also create a new file if
- # desired.
- # -------------------------------------------------------------------------
- ##
- proc Bib::pickBibliography {{allowNew 1} {prompt "Pick a bibliography file"}} {
- set biblist [Bib::ListAllBibliographies]
- if {$allowNew} {
- lappend biblist {New file…}
- }
- global Bib::_defaultBib
- set bibfile [listpick -p $prompt -L ${Bib::_defaultBib} $biblist]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } elseif {$bibfile == "New file…" } {
- set bibfile [putfile "Save new bibliography as…" ".bib"]
- if {$bibfile == ""} {
- error "No bibliography file selected."
- } else {
- set fout [open $bibfile w]
- close $fout
- }
- }
- return [file tail [set Bib::_defaultBib $bibfile]]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::ListAllBibliographies" --
- #
- # Return all bibliographies on the search path. Optionally only return
- # those which are in a given .aux file.
- # -------------------------------------------------------------------------
- ##
- proc Bib::ListAllBibliographies { {auxfile ""} } {
- #
- # with the pref vars I have eliminate all the TeX Paths scanning.
- # Furthermore, I can add the bibliography in the same directory as the
- # original LaTeX doc, and any bibliography in the modeSearchPath.
- #
- global BibmodeVars
- set biblist {}
- if {$BibmodeVars(useTexPaths)} {
- set biblist [concat $biblist [Bib::ListTexPathBibs auxfile]]]
- }
- if {$BibmodeVars(useModePaths)} {
- set biblist [concat $biblist [Bib::ListModePathBibs]]
- }
- if {$BibmodeVars(useCurrentPath)} {
- set biblist [concat $biblist [Bib::ListCurrentPathBibs]]
- }
- return [lunique $biblist]
- }
-
- proc Bib::ListCurrentPathBibs {} {
- global mode
- set biblist {}
- if {$mode == "TeX" || $mode == "Bib"} {
- # we should add the current window's path to the search path
- eval lappend biblist \
- [glob -nocomplain [file join [file dirname [win::Current]] *.bib]]
- }
- return $biblist
- }
-
-
- proc Bib::ListModePathBibs {} {
- set biblist {}
- foreach d [mode::getSearchPath] {
- eval lappend biblist [glob -nocomplain [file join ${d} *.bib]]
- }
- return $biblist
- }
-
- proc Bib::ListTexPathBibs { {auxfile ""} } {
- TeXEnsureSearchPathSet
- global AllTeXSearchPaths
- set biblist {}
- if {$auxfile == "" || [catch {set fid [open "$auxfile" r]}]} {
- foreach d $AllTeXSearchPaths {
- eval lappend biblist [glob -nocomplain [file join ${d} *.bib]]
- }
- } else {
- set bibs {}
- # get list of bibs from .aux file
- set cid [scancontext create]
- scanmatch $cid {bibdata\{([^\}]*)\}} {
- eval lappend bibs [split $matchInfo(submatch0) ","]
- }
- scanfile $cid $fid
- close $fid
- scancontext delete $cid
- # find the full paths
- foreach b $bibs {
- foreach d $AllTeXSearchPaths {
- if [file exists [file join ${d} ${b}.bib]] {
- lappend biblist [file join ${d} ${b}.bib]
- break
- }
- }
- }
- }
- return $biblist
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::GotoEntry" --
- #
- # Look for a bib entry in the given list of files, or if that fails or
- # isn't given, look in all available bib files on the search path.
- # -------------------------------------------------------------------------
- ##
- proc Bib::GotoEntry {entry {biblist {}}} {
- if ![catch {Bib::gotoEntryFromIndex $entry}] {
- return
- }
- if {[llength $biblist] && ![catch {Bib::_GotoEntry $entry $biblist 0}]} {
- return
- }
- if ![catch {Bib::_GotoEntry $entry [Bib::ListAllBibliographies]}] {
- return
- }
- beep
- error "Can't find entry '$entry' in the .bib file(s)"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::gotoEntryFromIndex" --
- #
- # Look in the bibIndex and find an entry very quickly.
- # -------------------------------------------------------------------------
- ##
- proc Bib::gotoEntryFromIndex {entry} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- global PREFS
- # if it fails, but we succeed later, we will have the opportunity
- # to rebuild the bibIndex
- if [file exists [file join ${PREFS} bibIndex]] {
- source [file join ${PREFS} bibIndex]
- foreach f [array names bibIndex] {
- if [regexp "\[ \r\n\]$entry\[ \r\n\]" "$bibIndex($f)"] {
- file::openQuietly $f
- set p [search -s -f 1 -r 1 $bibTopPat$entry [minPos]]
- eval select $p
- refresh
- eval select $p
- unset bibIndex
- return
- }
- }
- unset bibIndex
- }
- error "Entry '$entry' not found in bibIndex"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::_FindAllEntries" --
- #
- # Find all entries with a given prefix, optionally attaching the titles
- # of the entries (this requires a bibDatabase file to be setup). Used
- # by TeX citation completions: \cite{Darley<cmd-Tab>
- # -------------------------------------------------------------------------
- ##
- proc Bib::_FindAllEntries {eprefix {withtitles 1}} {
- global PREFS
- set matches {}
- if {$withtitles} {
- if {![file exists [file join ${PREFS} bibDatabase]]} {
- if {[askyesno "No bibDatabase exists, shall I make one?"]=="yes"} {
- Bib::rebuildDatabase
- } else {
- error "No bib database exists"
- }
- }
- set cid [scancontext create]
- scanmatch $cid "^${eprefix}" {
- lappend matches $matchInfo(line)
- }
- set fid [open [file join ${PREFS} bibDatabase] r]
- scanfile $cid $fid
- close $fid
- scancontext delete $cid
- } else {
- if ![file exists [file join ${PREFS} bibIndex]] {
- if {[askyesno "No bibIndex exists, shall I make one?"]=="yes"} {
- Bib::rebuildIndex
- } else {
- error "No bib index exists"
- }
- }
- global bibIndex
- if {![array exists bibIndex]} {
- source [file join ${PREFS} bibIndex]
- set unset 1
- }
- foreach f [array names bibIndex] {
- eval lappend matches [completion::fromList $eprefix "bibIndex(${f})"]
- }
- if {[info exists unset]} {unset bibIndex}
- }
- return $matches
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::_GotoEntry" --
- #
- # Find a bib entry in one of the given list of files, and signal an
- # error if the entry isn't found. I think this is the quickest way.
- # -------------------------------------------------------------------------
- ##
- proc Bib::_GotoEntry {entry biblist {rebuild 1}} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- set cid [scancontext create]
- scanmatch $cid $bibTopPat$entry {
- set found $matchInfo(offset)
- }
- set found ""
- foreach f $biblist {
- message "Searching [file tail $f]…"
- if {![catch {set fid [open $f]}]} {
- scanfile $cid $fid
- close $fid
- if {$found != ""} {
- file::openQuietly $f
- set found [pos::math [minPos] + $found]
- goto $found
- refresh
- select $found [nextLineStart $found]
- scancontext delete $cid
- global BibmodeVars
- # make the index since it was obviously out of date
- if {$rebuild == 1 && ($BibmodeVars(bibAutoIndex) == 2 \
- || [dialog::yesno "The bibIndex seems to be out of date. Rebuild?"])} {
- Bib::rebuildIndex
- }
- return
- }
- }
- }
- scancontext delete $cid
- error "Entry '$entry' not found."
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::rebuildIndex" --
- #
- # Build the bibIndex file which allows for very fast lookup of bib
- # entries.
- # -------------------------------------------------------------------------
- ##
- proc Bib::rebuildIndex {} {
- global PREFS
- set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
- set cid [scancontext create]
- # this will actually mark strings as well
- scanmatch $cid $bibTopPat2 {
- if {![regexp -nocase (preamble|string|comment) $matchInfo(submatch0)]} {
- lappend found $matchInfo(submatch1)
- }
- }
- set bout [open [file join ${PREFS} bibIndex] w]
- puts $bout "# Bibliography index file for quick reference lookup"
- puts $bout "# Created on [mtime [now]]"
- set bibs [lsort [Bib::ListAllBibliographies]]
- set bibl [llength $bibs]
- foreach f $bibs {
- set found {}
- puts $bout "set \"bibIndex($f)\" \{"
- message "Indexing ([incr bibl -1] left) [file tail $f]É"
- if {![catch {set fid [open $f]}]} {
- scanfile $cid $fid
- close $fid
- }
- # we sort so we can search it efficiently for all entries with
- # a given prefix.
- puts $bout " [lsort $found] "
- puts $bout "\}"
- }
- close $bout
- scancontext delete $cid
- message "bibIndex creation complete"
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "Bib::rebuildDatabase" --
- #
- # Build the bibDatabase which allows speedy completion of citations and
- # contains titles, so that you can pick the correct completion easily.
- # -------------------------------------------------------------------------
- ##
- proc Bib::rebuildDatabase {} {
- global PREFS
- set bdatout [open [file join ${PREFS} bibDatabase] w]
- puts $bdatout "# Bibliography database file for quick reference lookup"
- puts $bdatout "# Created on [mtime [now]]"
- # if it fails, but we succeed later, we will have the opportunity
- # to rebuild the bibIndex
- set bibs [lsort -ignore [Bib::ListAllBibliographies]]
- set bibl [llength $bibs]
- foreach f $bibs {
- file::openQuietly $f
- message "Indexing ([incr bibl -1] left) [file tail $f]…"
- puts $bdatout [Bib::makeDatabaseOf $f]
- killWindow
- }
- close $bdatout
- }
-
- proc Bib::indexOfThisWindow {{f ""}} {
- if {$f == ""} {
- set f [win::Current]
- }
- file::openQuietly $f
- set ret [Bib::makeDatabaseOf $f]
- new -n "* Index for [file tail $f] *" -m Text
- insertText $ret
- winReadOnly
- }
-
- proc Bib::addWinToDatabase {{f ""}} {
- if {$f == ""} {
- set f [win::Current]
- }
- global PREFS
- set bdatout [open [file join ${PREFS} bibDatabase] a]
- file::openQuietly $f
- puts $bdatout [Bib::makeDatabaseOf $f]
- close $bdatout
- }
-
- proc Bib::makeDatabaseOf {f} {
- set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
- message "Indexing ${f}…"
- set p [minPos]
- set ret ""
- while {![catch {search -s -f 1 -r 1 -- $bibTopPat $p} epos]} {
- set p [lindex $epos 0]
- set np [nextLineStart $p]
- set entry [getText $p $np]
- regexp {^@([a-zA-Z]+)([\{\(])[ ]*(.*)} $entry "" type brace entry
- if {[regexp -nocase (preamble|string|comment) $type] \
- || [catch {matchIt $brace [pos::math $p + [expr 3 + [string length $type]]]} end]} {
- set p $np
- continue
- }
- set p $end
- if {![catch {search -s -f 1 -r 1 -l $end -- "title\[ \t\]*=\[ \t\]*" $np} epos]} {
- set entry [string trim $entry "\{\( \t\r,"]
- set epos [lindex $epos 1]
- if {[regexp {[\(\{]} [lookAt $epos] brace] \
- && ![catch {matchIt $brace [pos::math $epos + 1]} end] } {
- set title [getText $epos $end]
- } else {
- set title [getText $epos [nextLineStart $epos]]
- }
- regsub -all "\[\{\}\]+" $title {} title
- regsub -all "\[ \n\r\t\]+" $title { } title
- append ret "$entry \{$title\}\r"
- }
- }
- return $ret
- }
-
-
- ###########################################################################
- # Menu command procs
- ###########################################################################
-
- proc makeField {menu item} {
- global fieldNames
- bibFormatSetup
-
- if {$item == "multipleFields"} {
- set flds [listpick -l -L {author year} -p "Pick desired fields:" $fieldNames]
- if {[llength flds]} {
- set lines {}
- foreach fld $flds {
- append lines [newField $fld]
- }
- } else {
- return
- }
- } else {
- set lines [newField $item]
- }
-
- goto [nextLineStart [getPos]]
- elec::Insertion $lines
- }
-
- proc makeEntry {menu item} {
- bibFormatSetup
- newEntry $item
- }
-
- ###########################################################################
- # Return the bounds of the bibliographic entry surrounding the current
- # position.
- #
- proc getEntry {pos} {
-
- set pos1 [search -f 0 -r 1 -n -s {[ ]*@[a-zA-Z]*[\{\(]} $pos ]
- if {$pos1 == ""} {
- set begPos [nextLineStart $pos]
- set endPos $begPos
- } else {
- set begPos [lineStart [lindex $pos1 0]]
- set pos0 [lindex $pos1 1]
- set openBrace [getText [pos::math $pos0 - 1] $pos0 ]
- if {[catch {matchIt $openBrace $pos0} pos1]} {
- alertnote "There seems to be a badly delimited field in here. Are entry and field delimiters set correctly?"
- goto $begPos
- error "Can't find close brace"
- } else {
- set endPos [nextLineStart $pos1]
- }
- }
- return [list $begPos $endPos]
- }
-
- ###########################################################################
- # Advance to the next bibliographic entry.
- #
- proc nextEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
-
- set pos0 [lindex [getEntry [getPos]] 1]
- set nextPos [nextLineStart $pos0]
-
- while {![catch {search -f 1 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [nextLineStart [lindex $pos 1]]
- }
- }
- goto $nextPos
- }
-
- ###########################################################################
- # Go back to the previous bibliographic entry.
- #
- proc prevEntry {} {
- global bibTopPat bibTopPat1 bibTopPat2
- # set topPat {[ ]*@([a-zA-Z]+)[\{\(]}
-
- set pos0 [lindex [getEntry [getPos]] 0]
- if {[pos::compare $pos0 > [minPos]]} {
- set nextPos $pos0
- set pos0 [pos::math $pos0 - 1]
- while {![catch {search -f 0 -r 1 -s $bibTopPat $pos0} pos]} {
- regexp $bibTopPat [eval getText $pos] mtch type
- if {$type != "string"} {
- set nextPos [lindex $pos 0]
- break
- } else {
- set pos0 [lineStart [lindex $pos 0]]
- if {[pos::compare $pos0 == [minPos]]} {break}
- set pos0 [pos::math $pos0 - 1]
- }
- }
- goto $nextPos
- }
- }
-
- ###########################################################################
- # Select (highlight) the current bibliographic entry.
- #
- proc selectEntry {} {
- set pos [getEntry [getPos]]
- select [lindex $pos 0] [lindex $pos 1]
- }
-
- ###########################################################################
- # Put the cite-key of the current entry on the clipboard.
- #
- proc copyCiteKey {} {
- global bibTopPat2
- set limits [getEntry [getPos]]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
- if {[regexp -indices $bibTopPat2 [getText $top $bottom] allofit type citekey]} {
- select [pos::math $top + [lindex $citekey 0]] [pos::math $top + [expr [lindex $citekey 1] + 1]]
- copy
- message "Copied \"[getSelect]\""
- }
- }
-
- ###########################################################################
- # Create a new bibliographic entry with its required fields.
- #
- proc newEntry {entryName} {
- global entryNames customEntries fieldNames rqdFld optFld myFld defFldVal
- global bibOpenEntry bibCloseEntry BibmodeVars
- goto [lindex [getEntry [getPos]] 1]
- if {$entryName == "customEntry"} {
- set lines "@••$bibOpenEntry••,\r"
- set theFields [listpick -l -L {author} -p "Pick desired fields:" $fieldNames]
- } else {
- set lines "@${entryName}$bibOpenEntry••,\r"
- if {[lsearch -exact $customEntries $entryName] >= 0 && [llength $myFld($entryName)]} {
- set theFields $myFld($entryName)
- } elseif {[lsearch -exact $entryNames $entryName] >= 0} {
- set theFields $rqdFld($entryName)
- } else {
- set theFields {}
- }
- }
- set nmlen 0
- foreach field $theFields {
- set len [string length $field]
- if {$len > $nmlen} {set nmlen $len}
- }
- set theTop [lineStart [getPos]]
- foreach field $theFields {
- catch {append lines [newField $field $nmlen]}
- }
- append lines "$bibCloseEntry\r"
- elec::Insertion $lines
- }
-
- ###########################################################################
- # Create a new field within the current bibliographic entry
- #
- proc newField {fieldName {nmlen 0}} {
- global fieldNames useBrace bibOpenQuote bibCloseQuote bibIndent
- global fieldDefs defFldVal
- set spc " "
- if {[lsearch -exact $fieldNames $fieldName] >= 0} {
- set needBraces $useBrace($fieldName)
- } else {
- set needBraces 1
- }
-
- if {[lsearch -exact $fieldDefs $fieldName] >= 0} {
- set val $defFldVal($fieldName)
- } else {
- set val "••"
- }
-
- if {$nmlen} {
- set pad [string range $spc 1 [expr $nmlen - [string length $fieldName]]]
- } else {
- set pad ""
- }
- if {$needBraces || $fieldName == "customField"} {
- set result "$bibIndent$fieldName =$pad ${bibOpenQuote}${val}${bibCloseQuote},\r"
- } else {
- set result "$bibIndent$fieldName =$pad $val,\r"
- }
- return $result
- }
-
- proc bibFormatSetup {} {
- global bibOpenQuote bibCloseQuote bibIndent BibmodeVars
- global bibOpenEntry bibCloseEntry bibAbbrevs
- bibFieldDelims
- bibEntryDelims
- set bibIndent $BibmodeVars(indentString)
- regsub {\\t} $bibIndent { } bibIndent
- set bibAbbrevs [listStrings]
- foreach abbrev $BibmodeVars(stdAbbrevs) {
- lappend bibAbbrevs [string tolower $abbrev]
- }
- }
-
- ###########################################################################
- # Find all entries that match a given regular expression and copy them to
- # a new buffer.
- #
- proc searchEntries {} {
- if [catch {prompt "Regular expression:" ""} reg] return
- if {![string length $reg]} return
- set reg ^.*$reg.*$
-
- set matches [findEntries $reg]
- if {[llength $matches] >0} {
- writeEntries $matches 0
- } else {
- message "No matching entries were found"
- }
- }
-
- ###########################################################################
- # Find all entries in which the indicated field matches a given regular
- # expression and copy them to a new buffer.
- #
- proc searchFields {} {
- global fieldNames
- if {[catch {eval prompt {{Field name:}} "author" {Fields} $fieldNames} fld]} return
- if {![string length $fld]} return
-
- if {[catch {prompt "Regular expression:" ""} reg]} return
- if {![string length $reg]} return
-
- set matches [findEntries $reg]
- if {[llength $matches] == 0} {
- return "No matching entries were found"
- }
-
- set vals {}
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- while {[set failure [expr {[getFldName $pos $top] != $fld}]] &&
- ![catch {search -f 1 -r 1 -i 1 -m 0 -l $bottom -s -- $reg $pos} mtch]} {
- set pos [lindex $mtch 1]
- }
- if {!$failure} { lappend vals [list $top $bottom] }
- }
-
- if {[llength $vals] >0} {
- writeEntries $vals 0
- } else {
- message "No matching entries were found"
- }
-
- }
-
- ###########################################################################
- # Sort all of the entries based on one of various criteria.
- #
- proc bibSortProc {menu item} {
- if {$item == "citeKey"} {
- sortByCiteKey
- } elseif {$item == "firstAuthor,Year"} {
- sortByAuthors 0 0
- } elseif {$item == "lastAuthor,Year"} {
- sortByAuthors 1 0
- } elseif {$item == "year,FirstAuthor"} {
- sortByAuthors 0 1
- } elseif {$item == "year,LastAuthor"} {
- sortByAuthors 1 1
- }
- }
-
- ###########################################################################
- # Sort the file marks. (These operations are also available under the
- # "Search:NamedMarks" menu)
- #
- proc markSortProc {menu item} {
- if {$item == "alphabetically"} {
- sortMarksFile
- } elseif {$item == "byPosition"} {
- orderMarks
- }
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by author.
- #
- proc sortByAuthors {{lastAuthorFirst 0} {yearFirst 0}} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set vals {}
- set others {}
- set refs {}
- set strs {}
-
- set beg [maxPos]
- set end [minPos]
-
- foreach hit $matches {
- set pos [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- if {[regexp $bibTopPat1 $entry allofit citeKey]} {
- set citeKey [string tolower $citeKey]
- set keyExists 1
- } else {
- set citekey ""
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citeKey] >= 0} {
- lappend refs [list $pos $top $bottom]
- } elseif {$bibSegStr && $keyExists && [lsearch -exact $strings $citeKey] >= 0} {
- lappend strs [list $citeKey $top $bottom]
- } else {
- if {![catch {getFldValue $entry author} fldval]} {
- if {[catch {getFldValue $entry year} year]} { set year 9999 }
- lappend vals [list [authSortKey $fldval $lastAuthorFirst $year $yearFirst] $top $bottom]
- } else {
- lappend others [list $pos $top $bottom]
- }
- }
- if {[pos::compare $top < $beg]} {set beg $top}
- if {[pos::compare $bottom > $end]} {set end $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs $others [lsort $vals] $refs]
- } else {
- set result [concat $others [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $beg $end
- } else {
- message "No results of author sort !!??"
- }
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listStrings {} {
- global bibTopPat bibTopPat1 bibTopPat2
- set matches [findEntries {^[ ]*@string *[\{\(]} 0]
-
- message "scanning for @strings…"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- regexp $bibTopPat1 $entry allofit citekey
- set citekey [string tolower $citekey]
- if {[catch {incr strings($citekey)} num]} {
- set strings($citekey) 1
- }
- }
- if {[catch {lsort [array names strings]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Return a list of the cite-keys of all cross-referenced entries.
- #
- proc listCrossrefs {} {
- set matches [findEntries {crossref}]
- catch {unset crossrefs}
-
- message "scanning for crossrefs…"
- foreach hit $matches {
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
- if {![catch {getFldValue $entry crossref} fldval]} {
- set fldval [string tolower $fldval]
- if {[catch {incr crossref($fldval)} num]} {
- set crossrefs($fldval) 1
- }
- }
- }
- if {[catch {lsort [array names crossrefs]} res]} {
- set res {}
- }
- message ""
- return $res
- }
-
- ###########################################################################
- # Create a sort key from an author list. When sorting entries by author,
- # performing the sort using keys should be faster than reparsing the author
- # lists for every comparison (the old method :-( ).
- #
- proc authSortKey {authList lastAuthorFirst {year {}} {yearFirst 0}} {
- global BibmodeVars
- set pat1 {\\.\{([A-Za-z])\}}
- set pat2 {\{([^\{\}]+) ([^\{\}]+)\}}
-
- # Remove enclosing braces, quotes, or whitespace
- set auths %[string trim $authList {{}" }]&
- # Remove TeX codes for accented characters
- regsub -all -- $pat1 $auths {\1} auths
- # Concatenate strings enclosed in braces
- while {[regsub -all $pat2 $auths {{\1\2}} auths]} {}
- # Remove braces (curly and square)
- regsub -all {[][\{\}]} $auths {} auths
- # regsub -all {,} $auths { ,} auths
- # Replace 'and's with begin-name/end-name delimiters
- regsub -all {[ ]and[ ]} $auths { \&% } auths
- # Put last name first in name fields without commas
- regsub -all {%([^\&,]+) ([^\&, ]+) *\&} $auths {%\2,\1\&} auths
- # Remove begin-name delimiters
- regsub -all {%} $auths {} auths
- # Remove whitespace surrounding name separators
- regsub -all {[ ]*\&[ ]*} $auths {\&} auths
- # Replace whitespace separating words with shrieks
- regsub -all {[ ,]+} $auths {!} auths
- # If desired, move last author to head of sort key
- if {$lastAuthorFirst} {
- regsub {(.*)&([^&]+)&?$} $auths {\2\&\1} auths
- }
- # If provided, sort by year (descending order) as well
- regsub {^[^0-9]*([0-9]*).*$} $year {\1} year
- if {$year != {}} {
- if {$BibmodeVars(descendingYears)} { catch {set year [expr 9999-$year]} }
- if {$yearFirst} {
- set auths "$year&$auths"
- } else {
- regsub {^([^&]+)(&?)} $auths "\\1\\&${year}\\2" auths
- }
- }
-
- return $auths
- }
-
- ###########################################################################
- # Sort all of the entries in the file alphabetically by their cite-keys.
- #
- proc sortByCiteKey {} {
- global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
- set bibSegStr $BibmodeVars(segregateStrings)
-
- set matches [findEntries $bibTopPat]
- set crossrefs [listCrossrefs]
- set strings [listStrings]
-
- set begEntries [maxPos]
- set endEntries [minPos]
-
- set strs {}
- set vals {}
- set refs {}
-
- foreach hit $matches {
- set beg [lindex $hit 0]
- set end [lindex $hit 1]
- set top [lindex $hit 2]
- set bottom [lindex $hit 3]
- if {[regexp $bibTopPat1 [getText $top $bottom] allofit citekey]} {
- set citekey [string tolower $citekey]
- set keyExists 1
- } else {
- set citekey "000000$beg"
- set keyExists 0
- }
-
- if {$keyExists && [lsearch -exact $crossrefs $citekey] >= 0} {
- lappend refs [list $top $top $bottom]
- } elseif {$keyExists && $bibSegStr && [lsearch -exact $strings $citekey] >= 0} {
- lappend strs [list $citekey $top $bottom]
- } else {
- lappend vals [list $citekey $top $bottom]
- }
-
- if {[pos::compare $top < $begEntries]} {set begEntries $top}
- if {[pos::compare $bottom > $endEntries]} {set endEntries $bottom}
- }
-
- if {$bibSegStr} {
- set result [concat $strs [lsort $vals] $refs]
- } else {
- set result [concat [lsort $vals] $refs]
- }
-
- if {[llength $result] >0} {
- writeEntries $result 1 $begEntries $endEntries
- } else {
- message "No results of cite-key sort !!??"
- }
- }
-
- ###########################################################################
- # Search for all entries matching a given regular expression. The results
- # are returned in a list, each element of which is a list of four integers:
- # the beginning and end of the matching entry and the beginning and end of
- # the matching string. Adapted from "matchingLines" in "misc.tcl".
- #
- proc findEntries {reg {casesen 1}} {
- if {![string length $reg]} return
-
- set pos [minPos]
- set result {}
- while {![catch {search -f 1 -r 1 -m 0 -i $casesen -s $reg $pos} mtch]} {
- set entry [getEntry [lindex $mtch 0]]
- lappend result [concat $mtch $entry]
- set pos [lindex $entry 1]
- }
- return $result
- }
-
- ###########################################################################
- # Return a list containing the data for the current entry, indexed by
- # the parameter names, e.g., "author", "year", etc. Index names for the
- # entry type and cite-key are "type" and "citekey".
- #
- proc getFields {pos} {
- global bibTopPat bibTopPat1 bibTopPat2 bibTopPat3
- set fldPat {[ ]*([a-zA-Z]+)[ ]*=[ ]*}
-
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- set entry [getText $top $bottom]
- regsub -all "\[\n\r\]+" $entry { } entry
- regsub -all "\[ \]\[ \]+" $entry { } entry
- #
- regsub {[, ]*[\)\}][ ]*$} $entry { } entry
-
- if {[regexp -indices $bibTopPat2 $entry mtch theType theKey ]} {
- set key [string range $entry [lindex $theKey 0] [lindex $theKey 1]]
- set theRest [expr 1 + [lindex $mtch 1]]
- } elseif {[regexp -indices $bibTopPat3 $entry mtch theType aField]} {
- set key {}
- set theRest [lindex $aField 0]
- } else {
- error "Invalid entry"
- }
- lappend names type
- set type [string tolower [string range $entry [lindex $theType 0] [lindex $theType 1]]]
- lappend data [list $type]
-
- lappend names citekey
- lappend data $key
-
- set entry ",[string range $entry $theRest end]"
- set fldPat {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set name {}
- while {[regexp -indices $fldPat $entry mtch sub1]} {
- set nextName [string range $entry [lindex $sub1 0] [lindex $sub1 1]]
- lappend names [string tolower $nextName]
- if {$name != ""} {
- set prevData [string range $entry 0 [expr [lindex $mtch 0]-1]]
- lappend data [breakIntoLines [bibFieldData $prevData]]
- }
- set name $nextName
- set entry [string range $entry [expr [lindex $mtch 1]+1] end]
- }
-
- lappend data [breakIntoLines [bibFieldData $entry]]
-
- return [list $names $data]
- }
-
- proc bibFieldData {text} {
- set text [string trim $text { ,#}]
- set text1 [string trim $text {\{\}\" }]
-
- if {[string match {*[\{\}\"]*} $text1]} {
- set words [parseWords $text]
- if {[llength $words]==1} {
- regsub {^[\{\"\']} $text {} text
- regsub {[\}\"\']$} $text {} text
- }
- } else {
- set text $text1
- }
- return $text
- }
-
-
- ###########################################################################
- # Extract the data from the indicated field of an entry, which is passed
- # as a single string. This version tries to be completely general,
- # allowing nested braces within data fields and ignoring escaped
- # delimiters. (derived from proc getField).
- #
- proc getFldValue {entry fldname} {
- set fldPat "\[ \]*${fldname}\[ \]*=\[ \]*"
- set fldPat2 {,[ ]*([^ =,]+)[ ]*=[ ]*}
- set slash "\\"
- set qslash "\\\\"
-
- set ok [regexp -indices -nocase $fldPat $entry mtch]
- if {$ok} {
- set pos [expr [lindex $mtch 1] + 1]
- set entry [string range $entry $pos end]
-
- if {[regexp -indices $fldPat2 $entry mtch sub1]} {
- set entry [string range $entry 0 [expr [lindex $mtch 0]-1]]
- }
- set fld [bibFieldData $entry]
-
- return $fld
-
- } else {
- error "field not found"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatEntry {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- set pos [getPos]
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {![catch {bibFormatEntry $pos} result]} {
- if {$result != [getText $top $bottom]} {
- replaceText $top $bottom $result
- } else {
- goto $bottom
- }
- goto [lindex [search -s -f 1 -r 1 "\[^ \t\r\n\]" [getPos]] 0]
- } else {
- message "Couldn't format this entry for some reason"
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it to the original
- # buffer in a canonical format
- #
- proc formatAllEntries {} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- set spc " "
-
- bibFormatSetup
-
- # This little dance handles the case that the first
- # entry starts on the first line
- #
- set hit [getEntry [getPos]]
- if {[pos::compare [lindex $hit 0] == [lindex $hit 1]]} {
- nextEntry
- set hit [getEntry [getPos]]
- }
-
- while {[pos::compare [getPos] < [lindex $hit 1]]} {
- set top [lindex $hit 0]
- set bottom [lindex $hit 1]
-
- if {![catch {bibFormatEntry $top} result]} {
- set oldEntry [getText $top $bottom]
- if {$result != $oldEntry} {
- deleteText $top $bottom
- insertText $result
- }
- }
- goto $top
- nextEntry
- set hit [getEntry [getPos]]
- }
- }
-
- ###########################################################################
- # Parse the entry around position "pos" and rewrite it in a canonical format.
- # The formatted entry is returned.
- #
- proc bibFormatEntry {pos} {
- global useBrace bibOpenQuote bibCloseQuote
- global bibOpenEntry bibCloseEntry bibIndent
- global rqdFld optFld BibmodeVars bibAbbrevs
- set spc " "
- #
- # note: calling proc must call "bibFormatSetup" before calling "bibFormatEntry"
- #
- set limits [getEntry $pos]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- if {[catch {getFields $pos} flds]} {
- error "bibFormatEntry: Getflds couldn't find any"
- }
-
- set names [lindex $flds 0]
- set vals [lindex $flds 1]
- set nfld [llength $names]
-
- set type [string tolower [lindex $vals 0]]
- set citekey [lindex $vals 1]
- # message "$citekey"
- # Don't process @string entries
- if {$type == "string"} {
- set lines [getText $top $bottom]
- return $lines
- }
- # Find length of longest field name
- set nmlen 0
- foreach nm $names {
- set len [string length $nm]
- if {$len > $nmlen} { set nmlen $len }
- if {![info exists useBrace($nm)]} { set useBrace($nm) 0 }
- }
-
- # Format first line
- set lines "@${type}${bibOpenEntry}${citekey},\r"
-
- # Format each field on a separate line
- for {set ifld 2} {$ifld < $nfld} {incr ifld} {
- set nm [lindex $names $ifld]
- set vl [lindex $vals $ifld]
- if {$vl != "" || ! $BibmodeVars(zapEmptyFields) ||
- [lsearch $rqdFld($type) $nm] >= 0} {
- set pad [expr $nmlen - [string length $nm]]
-
- if {$BibmodeVars(alignEquals)} {
- set pref "${bibIndent}$nm[string range $spc 1 $pad] ="
- } else {
- set pref "${bibIndent}$nm =[string range $spc 1 $pad]"
- }
- set ind [string range $spc 1 [string length $pref]]
-
- # Delimit field, if appropriate
- set noBrace [expr ($useBrace($nm) == 0 && [is::UnsignedInteger $vl]) || [regexp {\#} $vl]]
- if {$noBrace == 0 && [string first " " $vl] < 0} {
- set noBrace [expr [lsearch $bibAbbrevs [string tolower $vl]] >= 0]
- }
- if {$noBrace != 0} {
- set vl "$vl,"
- } else {
- set vl "${bibOpenQuote}${vl}${bibCloseQuote},"
- }
-
- set pieces [split $vl "\r"]
- append lines "$pref [lindex $pieces 0]\r"
- foreach piece [lrange $pieces 1 end] {
- append lines "$ind $piece\r"
- }
- }
- }
- append lines "$bibCloseEntry\r"
- return $lines
- }
-
- ###########################################################################
- # Get the name of the field that starts before the given position,
- # $pos. The positions $top and $bottom restrict the range of the
- # search for the beginning and end of the field; typically, $top and
- # $bottom will be the limits of a given entry.
- #
- proc getFldName {pos top} {
- set fldPat {[, ]+([^ =,\{\}\"\']+)[ ]*=[ ]*}
- if {![catch {search -f 0 -r 1 -m 0 -i 1 -s -limit $top "$fldPat" $pos} mtch]} {
- set theText [eval getText $mtch]
- regexp -nocase $fldPat $theText allofit fldnam
- return $fldnam
- } else {
- return {citekey}
- }
- }
-
- ###########################################################################
- # Set the quote characters for quoted fields based on the value of the
- # flag $bibUseBrace
- #
- proc bibFieldDelims {} {
- global BibmodeVars bibOpenQuote bibCloseQuote
- if {$BibmodeVars(fieldBraces)} {
- set bibOpenQuote "{"
- set bibCloseQuote "}"
- } else {
- set bibOpenQuote {"}
- set bibCloseQuote {"}
- }
- }
-
- proc bibEntryDelims {} {
- global BibmodeVars bibOpenEntry bibCloseEntry
- if {$BibmodeVars(entryBraces)} {
- set bibOpenEntry "{"
- set bibCloseEntry "}"
- } else {
- set bibOpenEntry "("
- set bibCloseEntry ")"
- }
- }
-
- proc isBibFile {} {
- set fileName [win::Current]
- set ext [file extension $fileName]
- return [string match ".bib" [string tolower $ext]]
- }
-
-
-
- ###########################################################################
- # Take a list of lists that point to selected entries and copy these into
- # a new window. The beginning and ending positions for each entry must
- # be the last two items in each sublist. The rest of the sublists are
- # ignored. It is assumed that each sublist has the same number of items.
- #
- proc writeEntries {entryPos nondestructive {beg {0}} {end {-1}}} {
- global BibmodeVars
- if {$end < 0} {set end [maxPos]}
- set llen [expr [llength [lindex $entryPos 0]] - 1]
- set llen1 [expr {$llen-1}]
- foreach entry $entryPos {
- set limits [lrange $entry $llen1 $llen]
- append lines [eval getText $limits]
- }
- set overwriteOK [expr $nondestructive || ! [isBibFile]]
- if {$BibmodeVars(overwriteBuffer) && $overwriteOK} {
- deleteText $beg $end
- insertText $lines
- goto $beg
- } else {
- set begLines [getText [minPos] [lineStart $beg]]
- set endLines [getText [nextLineStart $end] [maxPos]]
- new -n {*BibTeX Sort/Search*} -m Bib
- insertText $begLines
- insertText $lines
- insertText $endLines
- goto $beg
- setWinInfo dirty 0
- catch shrinkWindow
- }
- }
-
- ###########################################################################
- # Set a named mark for each entry, using the cite-key name
- #
- proc Bib::MarkFile {} {
- global BibmodeVars
- global bibTopPat bibTopPat1 bibTopPat2
- set pos [minPos]
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat1 $pos} res]} {
- set start [lindex $res 0]
- set pos [nextLineStart $start]
- set text [getText $start $pos]
- if {[regexp $bibTopPat2 $text mtch type citekey]} {
- if {[string tolower $type] != "string" || $BibmodeVars(markStrings)} {
- setNamedMark $citekey [lineStart [pos::math $start - 1]] $start $start
- }
- }
- }
- }
-
- ###########################################################################
- # Report the number of entries of each type
- #
- proc countEntries {} {
- global entryNames
- global bibTopPat bibTopPat1 bibTopPat2
-
- set pos [minPos]
- set count 0
- catch {unset type}
-
- while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat $pos} res]} {
- incr count
- set start [lindex $res 0]
- set end [nextLineStart $start]
- set text [getText $start $end]
- set lab ""
- if {[regexp $bibTopPat $text mtch entryType]} {
- set entryType [string tolower $entryType]
- if {[catch {incr type($entryType)} num]} {
- set type($entryType) 1
- }
- }
- set pos $end
- }
- new -n {*BibTeX Statistics*} -m Bib
- foreach name [lsort [array names type]] {
- if {$type($name) > 0} {
- append lines [format "%4.0d %s\n" $type($name) $name]
- }
- }
- append lines "---- -----------------\n"
- append lines [format "%4.0d %s\n" $count "Total entries"]
- insertText $lines
- goto [minPos]
- setWinInfo dirty 0
- catch {shrinkWindow 1}
- }
- #--------------------------------------------------------------------------
- # command-double-clicking:
- #--------------------------------------------------------------------------
-
- ###########################################################################
- # In Bib mode, Cmd-double-clicks resolve abbrevs and cross-refs
- #
- proc Bib::DblClick {from to} {
- global bibTopPat bibTopPat1 bibTopPat2
-
- set limits [getEntry $from]
- set top [lindex $limits 0]
- set bottom [lindex $limits 1]
-
- # Extend selection to largest string that could be an entry reference
- set text [string trim [eval getText [BibExtendClick $from $to $top $bottom]]]
-
- # Get the citekey of current entry, so we can avoid jumping to it
- set citekey {}
- regexp $bibTopPat2 [getText $top $bottom] mtch type citekey ]
- set fldName [getFldName $from $top]
-
- if {[string length $text] == 0 || $text == $citekey || $fldName == $text ||
- ($fldName == "citekey" && [string tolower $type] != "string")} {
- message "Command-double-click on abbreviations and crossref arguments"
- return
- }
-
- # Jump to the mark for the specified citation, if a mark exists...
- # ...otherwise, do an ordinary search for the cite key
- pushPosition
- set searchPat "$bibTopPat\[ \]*[quote::Regfind $text]\[ ,\}\)\]"
- if {![catch {search -f 1 -r 1 -i 1 -m 0 $searchPat 0} mtch]} {
- goto [lindex $mtch 0]
- } else {
- popPosition
- select $from $to
- if {$fldName == "crossref"} {
- message "Cross-reference \"$text\" not found"
- } else {
- message "Command-double-click on abbreviations and crossref arguments"
- }
- return
- }
- message "Use Ctl-. to return to original position"
- return
- }
-
- # Extend the selection around the initial selection {$from,$to}
- # Extension is restricted to the range {$top,$bottom} (the current entry)
- proc BibExtendClick {from to top bottom} {
- if {$to == [minPos]} { set to $from }
- set result [list $from $to]
- if {![catch {search -f 0 -r 1 -s -m 0 -l $top "\[,\{\]\"\'=" $from} mtch0]} {
- if {![catch {search -f 1 -r 1 -s -m 0 -l $bottom "\[,\}\]\"\'=" $to} mtch1]} {
- set from [lindex $mtch0 1]
- set to [lindex $mtch1 0]
- # Check for illegal chars embedded in the selection
- if {[regexp "\[\{\}\]=" [getText $from $to]] == 0} {
- set result [list $from $to]
- }
- }
- }
- return $result
- }
-
- #===============================================================================
- proc pcite {} {
- set words [getline "Citation keys" ""]
- if {![llength $words]} {error "No keys"}
-
- set pattern {@}
- foreach w $words {
- append pattern "(\[^@\]+$w)"
- }
-
- foreach entry [findEntries $pattern] {
- set res [getFields [lindex $entry 0]]
- set title [lindex [lindex $res 1] [lsearch [lindex $res 0] "title"]]
- set citekey [lindex [lindex $res 1] [lsearch [lindex $res 0] "citekey"]]
- set matches($title) $citekey
- set where($title) [lindex $entry 0]
- }
- if {![info exists matches]} {alertnote "No citations"; return}
- set title [listpick -p "Citation?" [lsort [array names matches]]]
- putScrap $matches($title)
- alertnote $matches($title)
- goto $where($title)
- }
-
-
-