[OS X TeX] Bibliography Citations (was: New TeXShop macro: "Insert reference")
Matthew Hills
hills_tex at tina.stanford.edu
Tue Aug 24 00:03:03 EDT 2004
Interesting to see all of these approaches to citations.
Here's my rundown on the recent contributions:
Thomas' applescript:
- clean parsing of BibTeX files (leverages David Kotz's scripts,
which runs BibTeX with custom style files)
- grabs all *.bib files in project directory
- puts a few extra files into /tmp
- no filtering of output
Joachim's TCLSH script:
- REGEX search for key definitions in *.bib files
Claus recommended BibDesk's "Complete Citation" service:
- this seems promising if you are already using BibDesk
- very fast to lookup entries
- slow to cut out entries that you don't want
- lookup is performed by title (and not author,
although there is another service to lookup by cite key)
- would also be nice to have some sort of browser pop up instead...
In the recent spirit of the group, I'm appending an applescript that I
wrote this
evening while thinking about some of these things:
- searches the project files to find the included bibliography files
- the current text selection is used to filter the suggested BibTeX
entries
(this filtering is case sensitive, but compares against all
fields)
Any suggestions on the proper search path for the bibtex files? (where
does
BibTeX look for files other than the current directory?)
Thanks,
Matt
PS--anyone following the "software for writing manuals" over on
scitech at lists.apple.com?
-- Applescript
-- ---------------------------------------------------------------
-- Cite Bibliography
-- ---------------------------------------------------------------
-- This script is designed to run from TeXShop
-- and will try to open the bibliography file and
-- show the user a list of possible items to cite.
-- ----------------------------------------
-- Properties
-- ----------------------------------------
-- Search Options: (list of strings)
-- a list of strings, with possible entries:
-- "sort" -- will sort the output
-- "title" -- will show both the reference and the title
property search_options : {"sort", "title"}
-- cite_cmd: (string)
-- the command that is used when placing text back into TeXShop
-- using "\\cite" will produce "\cite{tag}" in the editor
property cite_cmd : "\\cite"
-- limit_by_selection: (true/false)
-- If this is true, the current selection in TeXShop will
-- be used to filter the BibTeX entries that are displayed in
the dialog.
-- This filtering is case-sensitive, but can match any field in
the BibTeX
-- entry (title, keywords, etc).
property limit_by_selection : true
-- front_app: (string)
-- this is used in a number of places to make sure the dialogs
come up in TeXShop
property front_app : "TeXShop"
-- title_delim: (string)
-- if the "title" option is enabled, this string is used to
separate the
-- reference from the title displayed in the dialog box.
property title_delim : " -- "
-- ----------------------------------------
-- Dev Notes
-- ----------------------------------------
-- Original Version: 8/23/2004 - mdh
-- To-do:
-- * can improve O() by searching for other tags
-- while collecting project files
-- * Should check TeX's search path when searching for files
-- (currently only looking in project directory)
on run
try
set search_pattern to ""
tell application "TeXShop"
set f to ((path of front document as string) as POSIX file)
if limit_by_selection then
set search_pattern to the content of the selection of the front
document
end if
end tell
tell application "Finder" to set current_folder to the container of
(f as alias)
set rootfile to get_project_root(f)
set project_files to get_project_files(rootfile, current_folder)
set bib_files to get_bib_file(project_files, current_folder)
set cite_ref to query_for_citation(bib_files, search_pattern,
search_options)
if (cite_ref "") then
tell application "TeXShop" -- not sure why name is needed here
explicitly
set the selection of the front document to (cite_cmd & "{" &
cite_ref & "}") as string
end tell
end if
on error e
tell application front_app
display dialog ("Error: " & e) buttons {"Okay"}
end tell
end try
end run
-- --------------------------------
-- get_project_root
-- --------------------------------
-- given an alias to the current TeX file,
-- will check if their is a valid *.texshop file
-- and will return that, otherwise will return the
-- current file
on get_project_root(f)
try
set root_texshop to (f as string) & "shop"
tell application "Finder" to set root_texshop_alias to (root_texshop
as alias)
set fnum to (open for access root_texshop_alias)
set texshop_data to (read fnum as text)
close access fnum
tell application "Finder" to set current_folder to the container of
(f as alias) as string
set new_root to current_folder & texshop_data
return (new_root as alias)
on error e
return f
end try
end get_project_root
-- --------------------------------
-- get_bib_file
-- --------------------------------
-- given a list of .tex files, will
-- search for \bibliography statement(s)
-- and return a list of aliases to bib files
on get_bib_file(f_list, project_folder)
set bib_filenames to {}
set bib_files to {}
repeat with tex_file in f_list
set new_bib_arg to extract_tags(tex_file, "\\\\bibliography")
-- Note that there can be multiple bib files declared:
\bibliography{file1,file2}
-- so we need to split these up:
set old_delim to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
set new_bib_filenames to (every text item of (new_bib_arg as text))
set AppleScript's text item delimiters to old_delim
set bib_filenames to bib_filenames & new_bib_filenames
end repeat
repeat with bib_filename in bib_filenames
try
set new_file to ((project_folder as string) & bib_filename & ".bib")
as alias
if bib_files does not contain new_file then
set bib_files to bib_files & new_file
end if
end try
end repeat
return bib_files
end get_bib_file
-- --------------------------------
-- get_project_files
-- --------------------------------
-- given the head tex file, will return
-- a list of \input files, descending into
-- those files as needed
on get_project_files(root_file, project_folder)
set file_list to {root_file}
set file_indx to 1
repeat while file_indx ² (count (file_list))
set input_files to extract_tags((item file_indx of file_list),
"\\\\input")
set k to 1
repeat while k ² (count (input_files))
try
set new_file to ((project_folder as string) & ((item k of
input_files) as string) & ".tex") as alias
if file_list does not contain new_file then
set file_list to file_list & new_file
end if
end try
set k to k + 1
end repeat
set file_indx to file_indx + 1
end repeat
return file_list
end get_project_files
-- --------------------------------
-- extract_tags
-- --------------------------------
-- given a file and a tag to search for,
-- will return a list of the arguments to
-- the tag.
-- Notes:
-- (1) will not return multi-line argument
-- (2) does not match optional arguments e.g.,
\mytag[optional]{arg1}
-- (3) tag string should be escaped: eg, set tag to "\\\\input"
-- Explanation of search_cmd:
-- (1) remove comments
-- (2) convert internal {}: TAG{ ...{..}...} ==> TAG{
...(%...)%...}
-- (3) convert TAG{...} to "[space]%%"..."[space]%%%"
-- (4) remove all residual {} tokens
-- (5) restore %% and %%% to { and }, respectively
-- (6) remove characters outside of {...} pairs
-- (7) print out, with one set of contents per line
on extract_tags(f, tag)
set esc_newline to "\\
"
set search_cmd to " | sed -n -e '/^%/d;s/\\([^\\]\\)%.*/\\1/;/" & tag
& "{.*}/{' -e ':A' -e 's/\\(" & tag &
"{[^{}]*\\){\\([^{}]*\\)}/\\1(%\\2)%/g ;t A' -e 's/" & tag &
"{\\([^}]*\\)}/ %%\\1 %%%/g;s/[}{]//g;s/ %%%/}/g;s/
%%/{/g;s/^[^{]*{//g;s/}[^{]*$//g;s/}[^{]*{/" & esc_newline &
"/g;s/(%/{/g;s/)%/}/g;p;}'"
-- \\([^%]*\\)%)/{\\1}/g;p;}'"
set fpath to quoted form of (POSIX path of f)
set search_script to "tr '\\r' '\\n' < " & fpath & search_cmd
set results to (do shell script search_script)
if results = "" then
return {}
else
return every paragraph of results
end if
end extract_tags
-- --------------------------------
-- query_for_citation
-- --------------------------------
-- given a list of bibfile aliases, will
-- put up a dialog with a list of citations
-- and ask the user to select an item.
-- (see notes in parse_bibfile() for
-- comments on search_pattern and options)
on query_for_citation(f, search_pattern, options)
set bib_data to parse_bibfile(f, search_pattern, options)
set citation to ""
set bib_entry_count to (length of bib_data)
if (bib_entry_count = 0) then
tell application "Finder" to set bib_filename to the name of (the
first item of f)
tell application front_app
if search_pattern = "" then
display dialog ("Sorry, no BibTeX entries were found in the file
\"" & bib_filename & "\".") buttons {"Okay"}
else
display dialog ("Sorry, no BibTeX entries were found in the file
\"" & bib_filename & "\" that contain the pattern \"" & search_pattern
& "\".") buttons {"Okay"}
end if
end tell
else
if search_pattern = "" then
set my_prompt to "Choose from among the following " &
bib_entry_count & " BibTeX entries that were found:"
else
set my_prompt to "Choose from among the following " &
bib_entry_count & " BibTeX entries that were found that contain the
pattern \"" & search_pattern & "\":"
end if
tell application front_app
set user_selection to choose from list bib_data with prompt
my_prompt with multiple selections allowed
end tell
if user_selection false then
set AppleScript's text item delimiters to title_delim
set output_tags to {}
repeat with selected_item in user_selection
set output_tags to output_tags & (the first text item of
(selected_item as text) as string)
end repeat
set AppleScript's text item delimiters to ","
set citation to output_tags as string
end if
end if
return citation
end query_for_citation
-- --------------------------------
-- parse_bibfile
-- --------------------------------
-- given an alias to a bibfile, will return bibliography entries
--
-- If "search_pattern" is not "", will look for that pattern in
-- entry fields and will only return matching items. This is
-- case sensitive, so: parse_bibfile(f,"Smith",{}) will
-- match an entry with 'Author="J. Smith"', but would
-- not match against 'Author="J. smith"'
--
-- Options is a list of text items:
-- "sort" - to sort the output
-- "title" - to show the title
--
-- BUGS:
-- (1) not robust against all bibtex definitions like
"@string{...}"
on parse_bibfile(f, search_pattern, options)
if (class of f is list) then
set fpath to ""
repeat with f_item in f
set fpath to fpath & " " & (quoted form of (POSIX path of f_item))
end repeat
else
set fpath to quoted form of (POSIX path of f)
end if
if options contains "sort" then
set sort_cmd to " | sort -df"
else
set sort_cmd to ""
end if
if options contains "title" then
set set_title_action to "{split($0,line,\"=\");t=\"" & title_delim &
"\" line[2]}"
set title_cmd to "/title.*=.*/" & set_title_action & "/Title.*=.*/ "
& set_title_action & " /TITLE.*=.*/" & set_title_action
else
set title_cmd to ""
end if
if search_pattern "" then
-- Note that we handle two cases--when the search_pattern occurs
inside the citation's first line and when it doesn't
set limit_cmd to "/.*=.*" & search_pattern & "/{if (ref!=\"\"){f=1;}}"
set def_filt_cmd to "/^[ ]*@.*{.*,/ {if(f==1){print ref
t};split($0,line,\"{\");split(line[2],r2,\",\");ref=r2[1];f=0}/^[
]*@.*{.*" & search_pattern & ".*,/ {f=1}"
else
set limit_cmd to ""
set def_filt_cmd to "/^[ ]*@.*{.*,/ {if(f==1){print ref
t};split($0,line,\"{\");split(line[2],r2,\",\");ref=r2[1];f=1}"
end if
set parse_cmd to " | awk 'BEGIN { t=\"\"; ref=\"\"; f=0;}" &
def_filt_cmd & title_cmd & limit_cmd & "END{if(f==1){print ref t}}'"
set search_script to "cat " & fpath & " | tr '\\r' '\\n' " & parse_cmd
& sort_cmd
set results to (do shell script search_script)
if results = "" then return {}
return every paragraph of results
end parse_bibfile
--------------------- Info ---------------------
Mac-TeX Website: http://www.esm.psu.edu/mac-tex/
& FAQ: http://latex.yauh.de/faq/
TeX FAQ: http://www.tex.ac.uk/faq
List Post: <mailto:MacOSX-TeX at email.esm.psu.edu>
More information about the MacOSX-TeX
mailing list