From 5a3c5f14b38e5f88d24de338b80492bb2a591221 Mon Sep 17 00:00:00 2001
From: Mario Perez <mapreal19@gmail.com>
Date: Mon, 19 Jul 2021 16:21:36 +0200
Subject: [PATCH] feat: allows braces on citekey

---
 lib/bibtex/lexer.rb       | 11 +++++++----
 test/bibtex/test_lexer.rb | 18 +++++++++++-------
 2 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/lib/bibtex/lexer.rb b/lib/bibtex/lexer.rb
index ff57337..f255aa9 100644
--- a/lib/bibtex/lexer.rb
+++ b/lib/bibtex/lexer.rb
@@ -58,8 +58,8 @@ class Lexer
       string: /string/io,
       comment: /comment\b/io,
       preamble: /preamble\b/io,
-      key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-]+,}io,
-      optional_key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-]*,}io
+      key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-\{\}]+,}io,
+      optional_key: %r{\s*[[:alpha:][:digit:] /:_!$\?\.%+;&\*'"-\{\}]*,}io
     }
 
     MODE = Hash.new(:meta).merge(
@@ -120,7 +120,7 @@ def next_token
       @stack.shift
     end
 
-    # Returns true if the lexer is currenty parsing a BibTeX object.
+    # Returns true if the lexer is currently parsing a BibTeX object.
     def bibtex_mode?
       MODE[@mode] == :bibtex
     end
@@ -308,7 +308,10 @@ def enter_object
           push([:LBRACE, '{'])
           @mode = :content if @brace_level > 1 || @brace_level == 1 && active?(:comment)
 
-          push [:KEY, @scanner.matched.chop.strip] if @scanner.scan(Lexer.patterns[allow_missing_keys? ? :optional_key : :key])
+          if @scanner.scan(Lexer.patterns[allow_missing_keys? ? :optional_key : :key])
+            key = @scanner.matched.chop.strip.tr('{}', '')
+            push [:KEY, key]
+          end
         end
 
       else
diff --git a/test/bibtex/test_lexer.rb b/test/bibtex/test_lexer.rb
index 7dc100b..5904c91 100644
--- a/test/bibtex/test_lexer.rb
+++ b/test/bibtex/test_lexer.rb
@@ -7,33 +7,37 @@ class LexerTest < Minitest::Spec
     end
 
     it 'strips line breaks by default' do
-      Lexer.new.analyse(%(@string{ x = "foo\nbar" })).stack[-3].must_be :==,
+      _(Lexer.new.analyse(%(@string{ x = "foo\nbar" })).stack[-3]).must_be :==,
                                                                         [:STRING_LITERAL, 'foo bar']
     end
 
     it 'strips whitespace after line breaks by default' do
-      Lexer.new.analyse(%(@string{ x = "foo\n    bar" })).stack[-3].must_be :==,
+      _(Lexer.new.analyse(%(@string{ x = "foo\n    bar" })).stack[-3]).must_be :==,
                                                                             [:STRING_LITERAL, 'foo bar']
     end
 
     it 'matches KEY tokens' do
-      Lexer.new.analyse('@misc{foo, }').symbols.must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
+      _(Lexer.new.analyse('@misc{foo, }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
     end
 
     it 'matches KEY tokens with non-ascii characters' do
-      Lexer.new.analyse('@misc{löwe, }').symbols.must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
+      _(Lexer.new.analyse('@misc{löwe, }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
     end
 
     it 'matches KEY tokens after whitespace' do
-      Lexer.new.analyse('@misc{  foo, }').symbols.must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
+      _(Lexer.new.analyse('@misc{  foo, }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
+    end
+
+    it 'matches KEY tokens with braces' do
+      _(Lexer.new.analyse('@misc{foo:{123}, }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
     end
 
     it "doesn't start a comment for types starting with but not equal @comment" do
-      Lexer.new.analyse('@commentary{staudinger, }').symbols.must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
+      _(Lexer.new.analyse('@commentary{staudinger, }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :KEY, :RBRACE, false]
     end
 
     it "doesn't start a preamble for types starting with but not equal @preamble" do
-      Lexer.new.analyse('@preamblestring{ preamble }').symbols.must_be :==, [:AT, :NAME, :LBRACE, :NAME, :RBRACE, false]
+      _(Lexer.new.analyse('@preamblestring{ preamble }').symbols).must_be :==, [:AT, :NAME, :LBRACE, :NAME, :RBRACE, false]
     end
   end
 end