diff --git a/README.md b/README.md index 01930d7..810eafb 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ The main bibtex file ([cdl.bib](https://raw.githubusercontent.com/ContextLab/CDL - [`compare`](#compare) - [`commit`](#commit) - [Using the bibtex file as a common bibliography for all *local* LaTeX files](#using-the-bibtex-file-as-a-common-bibliography-for-all-local-latex-files) + - [General Unix/Linux Setup (Command Line Compilation)](#general-unixlinux-setup-command-line-compilation) + - [MacOS Setup with TeXShop and TeX Live](#macos-setup-with-texshop-and-tex-live) - [Using the bibtex file on Overleaf](#using-the-bibtex-file-on-overleaf) - [Acknowledgements](#acknowledgements) @@ -190,6 +192,8 @@ called, and a pull request must be submitted in order to integrate the changes into the main ContextLab fork. # Using the bibtex file as a common bibliography for all *local* LaTeX files + +## General Unix/Linux Setup (Command Line Compilation) 1. Check out this repository to your home directory 2. Add the following lines to your `~/.bash_profile` (or `~/.zshrc`, etc.): ``` @@ -208,6 +212,28 @@ latex filename pdflatex filename ``` +## MacOS Setup with TeXShop and TeX Live + +Mac GUI applications like TeXShop don't execute within your shell environment, which means the environment variable approach described above won't work when compiling through the TeXShop GUI. Instead, use TeX Live's built-in support for personal files: + +1. Check out this repository (we'll assume you cloned it to your home directory: `~/CDL-bibliography`) +2. Create the TeX Live personal texmf directory structure for bibliography files: +```bash +mkdir -p ~/Library/texmf/bibtex/bib +``` +3. Create a symbolic link from your personal texmf directory to the CDL-bibliography repository. **Important**: You must use the absolute path (not relative paths or `~`): +```bash +ln -s /Users/YOUR_USERNAME/CDL-bibliography/cdl.bib ~/Library/texmf/bibtex/bib/cdl.bib +``` +Replace `YOUR_USERNAME` with your actual macOS username, or use `$HOME` instead: +```bash +ln -s $HOME/CDL-bibliography/cdl.bib ~/Library/texmf/bibtex/bib/cdl.bib +``` +4. In your .tex file, use the line `\bibliography{cdl}` to generate a bibliography using the citation keys defined in cdl.bib +5. Compile your document using TeXShop's GUI or from the command line + +**Note**: This approach also works for command-line compilation, so you don't need to set up the environment variables if you use this method. + # Using the bibtex file on Overleaf You can use [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/) to maintain a reference to the cdl.bib file in this repository that you can easily keep in sync with latest version. This avoids the need to maintain a separate .bib file in each Overleaf project. diff --git a/bibcheck/helpers.py b/bibcheck/helpers.py index 84e64e9..6636de9 100644 --- a/bibcheck/helpers.py +++ b/bibcheck/helpers.py @@ -427,6 +427,21 @@ def roman2int(s): def valid_pages(p): + # Check for invalid dash characters (en-dash, em-dash, minus sign, etc.) + invalid_dashes = { + '\u2013': 'en-dash (–)', + '\u2014': 'em-dash (—)', + '\u2212': 'minus sign (−)', + '\u2010': 'hyphen (‐)', + '\u2011': 'non-breaking hyphen (‑)', + } + + for dash_char, dash_name in invalid_dashes.items(): + if dash_char in p: + # Return False with error message + suggested_fix = p.replace(dash_char, '-') + return False, [p, suggested_fix] + valid, kind, val = valid_page(p) if valid: #"single" page return True, [p, p] @@ -483,21 +498,39 @@ def format_journal_name(n, key=journal_key, force_caps=force_caps): #words = ['-'.join([format_journal_name(x) for x in w.split('-')]) if len(w.split('-')) > 1 else w for w in words] #deal with hyphens for i, w in enumerate(words): - words[i] = w.capitalize() - - #deal with hyphens - if len(w.split('-')) > 1: - words[i] = '-'.join(format_journal_name(c, key=key, force_caps=force_caps) for c in w.split('-')) - - if (i > 0) and (w.lower() in uncaps): - words[i] = words[i].lower() - - correct_caps = [f for f in force_caps if f.lower() == remove_non_letters(w.lower())] + # Check if word is fully braced (starts and ends with braces around the whole word) + is_fully_braced = before_letters(w, '{') and after_letters(w, '}') + + if is_fully_braced: + # Remove outer braces for processing, we'll check caps on the content + unbraced = remove_curlies(w, join=' ') + prefix, core, suffix = strip_leading_trailing_non_letters(unbraced) + else: + # Not fully braced, process normally + prefix, core, suffix = strip_leading_trailing_non_letters(w) + + # Skip if no core (word is only punctuation) + if not core: + words[i] = w + continue + + correct_caps = [f for f in force_caps if f.lower() == remove_non_letters(core.lower())] + if len(correct_caps) >= 1: c = correct_caps[-1] if not (c[0] == '{' and c[-1] == '}'): - c = insert_non_letters('{' + c + '}', remove_curlies(w, join=' ')) - words[i] = c + c = insert_non_letters('{' + c + '}', remove_curlies(core, join=' ')) + # Add back prefix and suffix (but not the outer braces we removed, since c already has them) + words[i] = prefix + c + suffix + else: + words[i] = w.capitalize() + + #deal with hyphens + if len(w.split('-')) > 1: + words[i] = '-'.join(format_journal_name(c, key=key, force_caps=force_caps) for c in w.split('-')) + + if (i > 0) and (w.lower() in uncaps): + words[i] = words[i].lower() return ' '.join(words) #rearrange author name (first middle last suffix) @@ -624,7 +657,36 @@ def before_letters(s, c): #true if c occurs before the first letter in s def after_letters(s, c): #true if c occurs after the last letter in s return before_letters(s[::-1], c) -def insert_non_letters(x, y): +def strip_leading_trailing_non_letters(s): + """Strip leading and trailing non-alphabetic characters from a string. + Returns (prefix, core, suffix) where core contains only letters and internal punctuation.""" + if len(s) == 0: + return '', '', '' + + # Find first letter + first_letter = -1 + for i, c in enumerate(s): + if c.lower() in ascii_lowercase: + first_letter = i + break + + if first_letter == -1: # No letters found + return s, '', '' + + # Find last letter + last_letter = -1 + for i in range(len(s) - 1, -1, -1): + if s[i].lower() in ascii_lowercase: + last_letter = i + break + + prefix = s[:first_letter] + core = s[first_letter:last_letter + 1] + suffix = s[last_letter + 1:] + + return prefix, core, suffix + +def insert_non_letters(x, y): z = '' i = 0 #position in x j = 0 #position in y @@ -641,15 +703,15 @@ def insert_non_letters(x, y): j += 1 else: #x[i] and y[j] are both in ascii_lowercase but x[i] != x[j] -- throw an error raise Exception(f'"{y}" is not a compatable template for "{x}"') - + #insert trailing punctuation from y if (j < len(y)) and (remove_non_letters(y[j:]) == ''): z += y[j:] - + #insert trailing punctuation from x if (i < len(x)) and (remove_non_letters(x[i:]) == ''): z += x[i:] - + return z def format_title(title): @@ -689,16 +751,22 @@ def ends_in_punctuation(s): #leave "a" and specified caps unchanged if w.lower() == 'a' or (before_letters(w, '{') and after_letters(w, '}')): - reformatted_title.append(w) + reformatted_title.append(w) #if w contains curly braces, just append it unchanged elif (w.count('{') > 0) or (w.count('}') > 0): - reformatted_title.append(w) + reformatted_title.append(w) else: - caps_match = [f for f in force_caps if f.lower() == remove_non_letters(w.lower())] - if len(caps_match) > 0: - w = insert_non_letters('{' + caps_match[-1] + '}', w) - elif not ends_in_punctuation(prev_w): - w = w.lower() + # Strip leading/trailing non-letters before checking caps + prefix, core, suffix = strip_leading_trailing_non_letters(w) + # Only check core if it has letters + if core: + caps_match = [f for f in force_caps if f.lower() == remove_non_letters(core.lower())] + if len(caps_match) > 0: + # Apply braces only to the core, then add back prefix and suffix + core_with_braces = insert_non_letters('{' + caps_match[-1] + '}', remove_curlies(core, join=' ')) + w = prefix + core_with_braces + suffix + elif not ends_in_punctuation(prev_w): + w = w.lower() reformatted_title.append(w) prev_w = w diff --git a/cdl.bib b/cdl.bib index 9fad678..a55a0e0 100644 --- a/cdl.bib +++ b/cdl.bib @@ -2530,7 +2530,7 @@ @article{StewVelu21 @inproceedings{CoppEtal17, author = {G Coppersmith and C Hilland and O Frieder and R Leary}, - booktitle = {2017 {IEEE} {EMBS} International Conference on Biomedical \& Health Informatics {(BHI})}, + booktitle = {2017 {IEEE} {EMBS} International Conference on Biomedical \& Health Informatics ({BHI})}, organization = {IEEE}, pages = {393--396}, title = {Scalable mental health analysis in the clinical whitespace via natural language processing}, @@ -26714,7 +26714,7 @@ @article{NokiEtal08 number = {1}, pages = {62--70}, publisher = {Elsevier}, - title = {Hippocampal theta (3--8hz) activity during classical eyeblink conditioning in rabbits}, + title = {Hippocampal theta (3--8{Hz}) activity during classical eyeblink conditioning in rabbits}, volume = {90}, year = {2008}} @@ -29328,7 +29328,7 @@ @article{Trei02 author = {Michel Treisman}, journal = {Psychonomic Bulletin and Review}, pages = {845--857}, - title = {Is signal detection theory fundamentally flawed? {A} response to {Balakrishnan} (1998a, 1998b, 1999)}, + title = {Is signal detection theory fundamentally flawed? {A} response to {Balakrishnan} (1998{A}, 1998{B}, 1999)}, volume = {9}, year = {2002}} @@ -34204,7 +34204,7 @@ @article{MilsEtal09 journal = {{PLoS} One}, number = {2}, pages = {e4338}, - title = {Neuronal shot noise and {B}rownian $1/f^2$ behavior in the local field potential}, + title = {Neuronal shot noise and {B}rownian $1/{F}^2$ behavior in the local field potential}, volume = {4}, year = {2009}} @@ -41137,7 +41137,7 @@ @article{TiitEtal93 journal = {Nature}, number = {6432}, pages = {59--60}, - title = {Selective attention enhances the auditory 40-hz transient response in humans}, + title = {Selective attention enhances the auditory 40-{Hz} transient response in humans}, volume = {364}, year = {1993}} @@ -45359,7 +45359,7 @@ @article{NeraBilk05 author = {L Nerad and D K Bilkey}, journal = {Journal of Neurophysiology}, pages = {1246--1254}, - title = {Ten- to 12-hz {EEG} oscillation in the rat hippocampus and rhinal cortex that is modulated by environmental familiarity}, + title = {Ten- to 12-{Hz} {EEG} oscillation in the rat hippocampus and rhinal cortex that is modulated by environmental familiarity}, volume = {93}, year = {2005}}