diff --git a/lib/jsduck/doc_links.rb b/lib/jsduck/doc_links.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4574d6d7203a2f314c47c640a2a9fe0db04b57de
--- /dev/null
+++ b/lib/jsduck/doc_links.rb
@@ -0,0 +1,47 @@
+module JsDuck
+
+ # Detects {@link ...} tags in text and replaces them with HTML links
+ # pointing to documentation. In addition to the href attribute
+ # links will also contain ext:cls and ext:member attributes.
+ class DocLinks
+ # Initializes instance to work in context of particular class, so
+ # that when {@link #blah} is encountered it knows that
+ # Context#blah is meant.
+ def initialize(context)
+ @context = context
+ end
+
+ # Replaces {@link Class#member link text} in given string with
+ # HTML links.
+ def replace(input)
+ input.gsub(/\{@link +(\S*?)(?: +(.+?))?\}/) do
+ target = $1
+ text = $2
+ if target =~ /^(.*)#(.*)$/
+ cls = $1.empty? ? @context : $1
+ member = $2
+ else
+ cls = target
+ member = false
+ end
+
+ # Construct link attributes
+ href = " href=\"output/#{cls}.html" + (member ? "#" + cls + "-" + member : "") + '"'
+ ext_cls = ' ext:cls="' + cls + '"'
+ ext_member = member ? ' ext:member="' + member + '"' : ""
+
+ # Construct link text
+ if text
+ text = text
+ elsif member
+ text = (cls == @context) ? member : (cls + "." + member)
+ else
+ text = cls
+ end
+
+ "" + text + ""
+ end
+ end
+ end
+
+end
diff --git a/spec/doc_links_spec.rb b/spec/doc_links_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..57a05b84fc2259cbd1c0ee4a73d06b9ce90822a1
--- /dev/null
+++ b/spec/doc_links_spec.rb
@@ -0,0 +1,38 @@
+require "jsduck/doc_links"
+
+describe JsDuck::DocLinks, "#parse" do
+
+ before do
+ @links = JsDuck::DocLinks.new("Context")
+ end
+
+ it "replaces {@link Ext.Msg} with link to class" do
+ @links.replace("Look at {@link Ext.Msg}").should ==
+ 'Look at Ext.Msg'
+ end
+
+ it "replaces {@link Foo#bar} with link to class member" do
+ @links.replace("Look at {@link Foo#bar}").should ==
+ 'Look at Foo.bar'
+ end
+
+ it "uses context to replace {@link #bar} with link to class member" do
+ @links.replace("Look at {@link #bar}").should ==
+ 'Look at bar'
+ end
+
+ it "allows use of custom link text" do
+ @links.replace("Look at {@link Foo link text}").should ==
+ 'Look at link text'
+ end
+
+ it "leaves text without {@link...} untouched" do
+ @links.replace("Look at {@me here} too").should ==
+ 'Look at {@me here} too'
+ end
+
+ it "ignores unfinished {@link tag" do
+ @links.replace("unfinished {@link tag here").should ==
+ 'unfinished {@link tag here'
+ end
+end