Skip to content

Commit

Permalink
Feature: Links to dedicated targets wallyqs#107
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardoricho committed Oct 9, 2024
1 parent 2a40784 commit 776fe3c
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 49 deletions.
93 changes: 59 additions & 34 deletions lib/org-ruby/html_output_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ def inline_formatting(str)
quote_tags("</#{Tags[marker][:close]}>")
end
end
rewrite_targets(str)

if @options[:use_sub_superscripts]
@re_help.rewrite_subp str do |type, text|
Expand All @@ -353,28 +354,7 @@ def inline_formatting(str)
end
end

@re_help.rewrite_links str do |link, defi|
[link, defi].compact.each do |text|
# We don't support search links right now. Get rid of it.
text.sub!(/\A(file:[^\s]+)::[^\s]*?\Z/, "\\1")
text.sub!(/\Afile(|\+emacs|\+sys):(?=[^\s]+\Z)/, "")
end

# We don't add a description for images in links, because its
# empty value forces the image to be inlined.
defi ||= link unless link =~ @re_help.org_image_file_regexp

if defi =~ @re_help.org_image_file_regexp
defi = quote_tags "<img src=\"#{defi}\" alt=\"#{defi}\" />"
end

if defi
link = @options[:link_abbrevs][link] if @options[:link_abbrevs].has_key? link
quote_tags("<a href=\"#{link}\">") + defi + quote_tags("</a>")
else
quote_tags "<img src=\"#{link}\" alt=\"#{link}\" />"
end
end
rewrite_links(str)

if @output_type == :table_row
str.gsub!(/^\|\s*/, quote_tags("<td>"))
Expand All @@ -395,18 +375,7 @@ def inline_formatting(str)
end

# Reference footnote
@re_help.rewrite_footnote str do |label, content|
footnote = document.footnotes.find do |footnote|
footnote[:label] == label || footnote[:content] == content
end

a_id = (footnote[:label].nil? || footnote[:label].empty?) ? footnote[:index] : footnote[:label]
a_text = footnote[:index]
a_href = (footnote[:label].nil? || footnote[:label].empty?) ? footnote[:index] : footnote[:label]

footnote_tag = "<sup><a id=\"fnr.#{a_id}\" class=\"footref\" href=\"#fn.#{a_href}\" role=\"doc-backlink\">#{a_text}</a></sup>"
quote_tags(footnote_tag)
end
rewrite_footnote(str)
end

# Two backslashes \\ at the end of the line make a line break without breaking paragraph.
Expand All @@ -419,6 +388,62 @@ def inline_formatting(str)
str = @re_help.restore_code_snippets str
end

def rewrite_footnote(str)
@re_help.rewrite_footnote(str) do |label, content|
footnote = document.footnotes.find do |footnote|
footnote[:label] == label || footnote[:content] == content
end

a_id = (footnote[:label].nil? || footnote[:label].empty?) ? footnote[:index] : footnote[:label]
a_text = footnote[:index]
a_href = (footnote[:label].nil? || footnote[:label].empty?) ? footnote[:index] : footnote[:label]

footnote_tag = "<sup><a id=\"fnr.#{a_id}\" class=\"footref\" href=\"#fn.#{a_href}\" role=\"doc-backlink\">#{a_text}</a></sup>"
quote_tags(footnote_tag)
end
end

def rewrite_links(str)
@re_help.rewrite_links(str) do |link, defi|
[link, defi].compact.each do |text|
text.sub!(/\A(file:[^\s]+)::[^\s]*?\Z/, "\\1")
# We don't support search links right now. Get rid of it.
text.sub!(/\Afile(|\+emacs|\+sys):(?=[^\s]+\Z)/, "")
end

# We don't add a description for images in links, because its
# empty value forces the image to be inlined.
defi ||= link unless link =~ @re_help.org_image_file_regexp

if defi =~ @re_help.org_image_file_regexp
defi = quote_tags "<img src=\"#{defi}\" alt=\"#{defi}\" />"
end

if defi
link = @options[:link_abbrevs][link] if @options[:link_abbrevs].has_key?(link)
target = document.targets.find do |target|
puts "Find #{defi} target #{target}"
target[:content] == defi
end
link = "#tg.#{target[:index]}" if target
quote_tags("<a href=\"#{link}\">") + defi + quote_tags("</a>")
else
quote_tags "<img src=\"#{link}\" alt=\"#{link}\" />"
end
end
end

def rewrite_targets(line)
line.gsub!(RegexpHelper.target) do |_match|
match = Regexp.last_match
target = document.targets.find do |target|
target[:content] == match[:content]
end
target_tag = "<span id=\"tg.#{target[:index]}\">#{target[:content]}</span>"
quote_tags(target_tag)
end
end

def normalize_lang(lang)
case lang
when 'emacs-lisp', 'common-lisp', 'lisp'
Expand Down
4 changes: 4 additions & 0 deletions lib/org-ruby/line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ def footnote?
RegexpHelper.footnote_reference.match(@line)
end

def target?
RegexpHelper.target.match(@line)
end

def property_drawer_begin_block?
match = RegexpHelper.drawer.match(@line)
match && match[:name].downcase == 'properties'
Expand Down
8 changes: 8 additions & 0 deletions lib/org-ruby/line_regexp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ def metadata
/^\s*(CLOCK|DEADLINE|START|CLOSED|SCHEDULED):/
end

def org_link
/\[\[(?<url>[^\[\]]+)\](\[(?<friendly_text>[^\[\]]+)\])?\]/x
end

def property_item
/^\s*:(?<key>[\w\-]+):\s*(?<value>.*)$/
end
Expand Down Expand Up @@ -102,5 +106,9 @@ def table_separator
def tags
/\s*:(?<tags>[\w:@]+):\s*$/
end

def target
/<{2}(?<content>[\w\s]+)>{2}/
end
end
end
2 changes: 2 additions & 0 deletions lib/org-ruby/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ def parse_lines(lines)

# Store footnotes
document.store_footnote(line)
# Store targets
document.store_target(line)

if (line.end_block? && [line.paragraph_type, :comment].include?(mode)) ||
(line.property_drawer_end_block? && (mode == :property_drawer))
Expand Down
6 changes: 1 addition & 5 deletions lib/org-ruby/regexp_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def capture_footnote_definition(str)
# HTML-style link, and that is how things will get recorded in
# +result+.
def rewrite_links(str)
str.gsub!(org_link_regexp) do |_match|
str.gsub!(RegexpHelper.org_link) do |_match|
yield Regexp.last_match['url'], Regexp.last_match['friendly_text']
end
str.gsub!(org_angle_link_text_regexp) do |_match|
Expand All @@ -172,10 +172,6 @@ def org_emphasis_regexp
"(?=#{post_emphasis})")
end

def org_link_regexp
/\[\[(?<url>[^\[\]]+)\](\[(?<friendly_text>[^\[\]]+)\])?\]/x
end

def org_image_file_regexp
/\.(gif|jpe?g|p(?:bm|gm|n[gm]|pm)|svgz?|tiff?|x[bp]m)/i
end
Expand Down
14 changes: 13 additions & 1 deletion lib/orgmode/elements/document.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module Orgmode
module Elements
class Document
attr_reader :footnotes
attr_reader :footnotes, :targets

def initialize
@footnotes = []
@targets = []
end

def store_footnote(line)
Expand All @@ -29,6 +30,17 @@ def store_footnote(line)
footnote[:content] = content
end
end

def store_target(line)
return unless line.target?

line.to_s.scan(RegexpHelper.target) do |match|
content = match.first
target_index = @targets.length + 1
target = { index: target_index, content: content }
@targets.push(target)
end
end
end
end
end
3 changes: 3 additions & 0 deletions spec/html_examples/link-features.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ <h2>In these links, .org used to be converted to .html. Not anymore since 0.9.2
<h2>Links abbreviations</h2>
<p>URLs can be abbreviated by a LINK definition in the org file</p>
<p><a href="http://example.com/something">This is an abbreviated link example</a></p>
<h2>Internal links</h2>
<p>Links with plain text point to <a href="#tg.1">target</a> and each <a href="#tg.2">link</a> has it&#8217;s own id.</p>
<p>This is a <span id="tg.1">target</span> and the <span id="tg.2">link</span> has <code>tg.index</code> id.</p>
6 changes: 6 additions & 0 deletions spec/html_examples/link-features.org
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ sections within documents, etc.
#+LINK: example http://example.com/something

[[example][This is an abbreviated link example]]

* Internal links

Links with plain text point to [[target]] and each [[link]] has it's own id.

This is a <<target>> and the <<link>> has ~tg.index~ id.
16 changes: 16 additions & 0 deletions spec/org-ruby/line_regexp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ class DummyRegexp
it { expect(regexp.metadata).to match ' SCHEDULED:' }
end

describe '.org-link-regexp' do
it { expect(regexp.org_link).to match '[[url][description]]' }
it { expect(regexp.org_link).to match '[[url]]' }
it { expect(regexp.org_link).to match '[[a][~a~]]' }
end

describe '.property_item' do
it { expect(regexp.property_item).to match ':key:value' }
it { expect(regexp.property_item).to match ':key: value' }
Expand Down Expand Up @@ -234,5 +240,15 @@ class DummyRegexp
it { expect(regexp.tags).not_to match ":@tag :" }
it { expect(regexp.tags).not_to match "@tag:" }
end

describe '.target' do
it { expect(regexp.target).to match "<<Target>>" }
it { expect(regexp.target).to match "Line <<Target>>" }
it { expect(regexp.target).to match "<<Target>> end" }
it 'captures match content' do
match = regexp.target.match("Pre <<This is a target>> post")
expect(match[:content]).to eq "This is a target"
end
end
end
end
40 changes: 40 additions & 0 deletions spec/orgmode/elements/document_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'spec_helper'
module Orgmode
module Elements
RSpec.describe Document do
let(:document) { Document.new }

describe 'initialize' do
it 'has empty footnotes' do
expect(document.footnotes).to be_empty
end
it 'has empty targets' do
expect(document.targets).to be_empty
end
end

describe '#store_target' do
let(:target_content) { 'Target content' }
let(:target) { "<<#{target_content}>>" }
let(:line) { Line.new target }

it 'save target in targets' do
document.store_target line
saved_target = document.targets.last
expect(saved_target[:content]).to eq target_content
expect(saved_target[:index]).to be 1
end

context 'when line has several targets' do
let(:line) { Line.new "Line with <<more>> than <<one>> target." }

it "save all targets" do
document.store_target line
contents = document.targets.map { |target| target[:content] }
expect(contents).to include('more', 'one')
end
end
end
end
end
end
9 changes: 0 additions & 9 deletions spec/regexp_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,6 @@
example { expect(helper.org_emphasis_regexp).to match '[[a][~a~]]' }
end

describe '#org-link-regexp' do
it 'match org-links' do
expect(helper.org_link_regexp).to match '[[url][description]]'
end

example { expect(helper.org_link_regexp).to match '[[url]]' }
example { expect(helper.org_link_regexp).to match '[[a][~a~]]' }
end

it "should recognize simple markup" do
e = Orgmode::RegexpHelper.new
total = 0
Expand Down

0 comments on commit 776fe3c

Please sign in to comment.