Index: misc/refresh-cache.rb
===================================================================
--- misc/refresh-cache.rb	(.../vendor)	(revision 0)
+++ misc/refresh-cache.rb	(.../branches/wiki)	(revision 58)
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+#
+# $Id$
+#
+
+require 'getopts'
+
+def usage(status)
+  (status == 0 ? $stdout : $stderr).print(<<EOS)
+Usage: #{File.basename($0)}
+EOS
+  exit status
+end
+
+def main
+  ok = getopts(nil, 'help')
+  usage(0) if $OPT_help
+  usage(1) unless ok
+
+  load './bitchannelrc'
+  setup_environment
+  wiki = bitchannel_context()
+  wiki._repository.link_cache.clear
+  wiki._repository.revlink_cache.clear
+  wiki._repository.title_cache.clear
+  revlinks = {}
+  wiki._repository.pages.each do |page|
+    page.links
+    page.links.each do |dest|
+      (revlinks[dest] ||= []).push page.name
+    end
+  end
+  revlinks.each do |name, revlinks|
+    wiki._repository.revlink_cache[name] = revlinks
+  end
+  require 'bitchannel/syntax'
+  c = BitChannel::Syntax.new(wiki._config, wiki._repository)
+  titles = []
+  wiki._repository.pages.each do |page|
+    titles << [page.name, c.title(page.source)]
+  end
+  titles.each do |name, title|
+    wiki._repository.title_cache[name] = title
+  end
+end
+
+main

Property changes on: misc/refresh-cache.rb
___________________________________________________________________
Name: svn:keywords
   + Id

Index: misc/refresh-linkcache.rb
===================================================================
--- misc/refresh-linkcache.rb	(.../vendor)	(revision 58)
+++ misc/refresh-linkcache.rb	(.../branches/wiki)	(revision 58)
@@ -1,39 +0,0 @@
-#!/usr/bin/env ruby
-#
-# $Id$
-#
-
-require 'optparse'
-
-def main
-  parser = OptionParser.new
-  parser.on('--help') {
-    puts parser.help
-    exit 0
-  }
-  begin
-    parser.parse!
-  rescue OptionParser::ParseError => err
-    $stderr.puts err.message
-    $stderr.puts parser.help
-    exit 1
-  end
-
-  load './bitchannelrc'
-  setup_environment
-  wiki = bitchannel_context()
-  wiki._repository.link_cache.clear
-  wiki._repository.revlink_cache.clear
-  revlinks = {}
-  wiki._repository.pages.each do |page|
-    page.links
-    page.links.each do |dest|
-      (revlinks[dest] ||= []).push page.name
-    end
-  end
-  revlinks.each do |name, revlinks|
-    wiki._repository.revlink_cache[name] = revlinks
-  end
-end
-
-main
Index: lib/bitchannel/repository.rb
===================================================================
--- lib/bitchannel/repository.rb	(.../vendor)	(revision 58)
+++ lib/bitchannel/repository.rb	(.../branches/wiki)	(revision 58)
@@ -120,6 +120,7 @@
         conf.required! :cachedir
         @link_cache = LinkCache.new("#{conf[:cachedir]}/link".untaint)
         @revlink_cache = LinkCache.new("#{conf[:cachedir]}/revlink".untaint)
+        @title_cache = TitleCache.new("#{conf[:cachedir]}/title")
         @notifier = conf[:notifier]
       }
       # per-request cache
@@ -145,6 +146,7 @@
     # internal use only
     attr_reader :link_cache
     attr_reader :revlink_cache
+    attr_reader :title_cache
 
     def read_only?
       @read_only_p
@@ -153,12 +155,12 @@
     attr_reader :syntax
     attr_writer :syntax   # FIXME: tmp
 
-    def page_names
-      @wc_read.cvs_Entries.keys.map {|name| decode_filename(name) }
+    def page_names_and_titles
+      @wc_read.cvs_Entries.keys.map {|name| [decode_filename(name), name2title(name)] }
     end
 
     def pages
-      page_names().map {|name| new_page(name) }
+      page_names_and_titles().map {|name,| new_page(name) }
     end
 
     def orphan_pages
@@ -216,11 +218,16 @@
       new_page(name)
     end
 
-    def updated(name, new_rev, new_text)
+    def updated(name, new_rev, new_title, new_text)
       update_linkcache name, @syntax.extract_links(new_text)
+      update_titlecache name, new_title
       notify name, new_rev
     end
 
+    def name2title(name)
+      @title_cache[name] || name
+    end
+    
     def updated_externally(name)
       @wc_read.chdir {|wc|
         wc.cvs_update name
@@ -255,6 +262,10 @@
       }
     end
 
+    def update_titlecache(name, new_title)
+      @title_cache[name] = new_title
+    end
+
     def notify(name, new_rev)
       return unless @notifier
       # fork twice not to make zombie
@@ -277,7 +288,7 @@
   end   # class Repository
 
 
-  class LinkCache
+  class Cache
 
     include FilenameEncoding
     include LockUtils
@@ -292,42 +303,43 @@
       FileUtils.rm_rf @dir
       Dir.mkdir @dir
     end
-
-    def entries
-      Dir.entries(@dir)\
-          .reject {|ent| /\,tmp\z/ =~ ent }\
-          .select {|ent| File.file?("#{@dir}/#{ent.untaint}") }\
-          .map {|ent| decode_filename(ent) }
-    end
-
+    
     def [](name)
       read_cache(cache_path(name))
     end
 
-    def []=(name, links)
+    def []=(name, values)
       lock {
-        write_cache cache_path(name), links
+        write_cache cache_path(name), values
       }
     end
-
-    def add_link(name, lnk)
-      links = (read_cache(cache_path(name)) || [])
-      write_cache cache_path(name), (links + [lnk]).uniq.sort
+    
+    def updating
+      lock {
+        yield self
+      }
     end
+    
+    private
 
-    def del_link(name, lnk)
-      links = (read_cache(cache_path(name)) || [])
-      write_cache cache_path(name), (links - [lnk]).uniq.sort
+    def read_cache(path)
+      File.readlines(path).map {|line| line.strip.untaint }
+    rescue Errno::ENOENT
+      return nil
     end
 
-    def updating
-      lock {
-        yield self
+    def write_cache(path, values)
+      tmp = "#{path},tmp"
+      File.open(tmp, 'w') {|f|
+        values.each do |line|
+          f.puts line
+        end
       }
+      File.rename tmp, path
+    ensure
+      File.unlink tmp if File.exist?(tmp)
     end
 
-    private
-
     def lock
       if @locking
         yield
@@ -347,35 +359,71 @@
       "#{@dir}/#{encode_filename(name)}"
     end
 
-    def read_cache(path)
-      File.readlines(path).map {|line| line.strip.untaint }
-    rescue Errno::ENOENT
-      return nil
+  end # Class Cache
+
+  
+  class LinkCache < Cache
+
+    def entries
+      Dir.entries(@dir)\
+          .reject {|ent| /\,tmp\z/ =~ ent }\
+          .select {|ent| File.file?("#{@dir}/#{ent.untaint}") }\
+          .map {|ent| decode_filename(ent) }
     end
+    
+    def add_link(name, lnk)
+      links = (read_cache(cache_path(name)) || [])
+      write_cache cache_path(name), (links + [lnk]).uniq.sort
+    end
 
-    def write_cache(path, links)
-      tmp = "#{path},tmp"
-      File.open(tmp, 'w') {|f|
-        links.each do |lnk|
-          f.puts lnk
-        end
-      }
-      File.rename tmp, path
-    ensure
-      File.unlink tmp if File.exist?(tmp)
+    def del_link(name, lnk)
+      links = (read_cache(cache_path(name)) || [])
+      write_cache cache_path(name), (links - [lnk]).uniq.sort
     end
 
   end   # class LinkCache
 
+  
+  class TitleCache < Cache
+    
+    SEPARATOR = "\001"
+    CACHEFILE_NAME = 'name2title'
+    
+    def initialize(dir)
+      super
+      @cache = {}
+      (read_cache(cache_path(CACHEFILE_NAME)) || {})\
+        .map {|line| line.split(/#{SEPARATOR}/o)}\
+        .each {|name, title| @cache[name] = title}
+    end
 
+    def [](name)
+      @cache[name] || name
+    end
+    
+    def []=(name, title)
+      lock {
+        @cache[name] = title
+        write_cache cache_path(CACHEFILE_NAME), @cache.map {|item| item.join(SEPARATOR)}
+      }
+    end
+
+    def clear
+      super
+      @cache = {}
+    end
+    
+  end # Class TitleCache
+
+
   module CVSRevision
     def cvsrev_to_i(rev, on1111 = 1)
       return on1111 if rev == '1.1.1.1'
       rev.slice(/\A1\.(\d+)\z/, 1).to_i
     end
   end
+  
 
-
   class CVSWorkingCopy
 
     include FilenameEncoding
@@ -811,10 +859,12 @@
       @revision = nil
       @links = nil
       @revlinks = nil
+      @title = repository.name2title(name)
     end
 
     attr_reader :repository
     attr_reader :name
+    attr_reader :title
 
     def syntax
       @repository.syntax
@@ -917,7 +967,8 @@
       @wc_read.chdir {|wc|
         wc.cvs_update @name
       }
-      @repository.updated @name, new_rev, new_text
+      new_title = syntax.title(new_text) || @name
+      @repository.updated @name, new_rev, new_title, new_text
     end
 
     def edit
Index: lib/bitchannel/syntax.rb
===================================================================
--- lib/bitchannel/syntax.rb	(.../vendor)	(revision 58)
+++ lib/bitchannel/syntax.rb	(.../branches/wiki)	(revision 58)
@@ -25,6 +25,11 @@
       @interwikinames = nil
     end
 
+    def title(str)
+      first_line = str.slice(/\A.*/)
+      TITLE =~ first_line ? Regexp.last_match.post_match.strip : nil
+    end
+    
     def compile(str, page_name)
       @f = LineInput.new(StringIO.new(str))
       @page_name = page_name
@@ -70,6 +75,9 @@
     def DUMMY_REPOSITORY.inspect
       '#<dummy Repository object>'
     end
+    def DUMMY_REPOSITORY.name2title(name)
+      name
+    end
 
     def Syntax.extract_links(str)
       new(DUMMY_CONFIG, DUMMY_REPOSITORY)._extract_links(str)
@@ -91,6 +99,7 @@
     # Block
     #
 
+    TITLE = /\A=(?=[^=])/
     CAPTION = /\A(?:={2,4}|!{1,4})/
     UL = /\A\s*\*|\A-/
     OL = /\A\s*\(\d+\)|\A\#/   # should not allow spaces before '#'
@@ -105,6 +114,9 @@
         CITE, TABLE, PRE, INDENTED, BLOCKEXT)
 
     def do_compile
+      if @f.next? and TITLE =~ @f.peek
+        @f.gets # skip
+      end
       while @f.next?
         case @f.peek
         when CAPTION   then caption @f.gets
@@ -434,7 +446,7 @@
       return escape_html(name) if @repository.invalid?(name)
       @internal_links.push name
       if @repository.exist?(name)
-      then %Q[<a href="#{view_url(name)}">#{escape_html(name)}</a>]
+      then %Q[<a href="#{view_url(name)}">#{escape_html(@repository.name2title(name))}</a>]
       else %Q[<a href="#{view_url(name)}" class="dangling">?</a>#{escape_html(name)}]
       end
     end
Index: lib/bitchannel/asissyntax.rb
===================================================================
--- lib/bitchannel/asissyntax.rb	(.../vendor)	(revision 58)
+++ lib/bitchannel/asissyntax.rb	(.../branches/wiki)	(revision 58)
@@ -21,6 +21,10 @@
       @repository = repo
     end
 
+    def title(str)
+      nil
+    end
+    
     def extract_links(str)
       []
     end
Index: lib/bitchannel/page.rb
===================================================================
--- lib/bitchannel/page.rb	(.../vendor)	(revision 58)
+++ lib/bitchannel/page.rb	(.../branches/wiki)	(revision 58)
@@ -121,6 +121,10 @@
       escape_html(@page.name)
     end
 
+    def page_title
+      escape_html(@page.title)
+    end
+
     def page_url
       escape_url(@page.name)
     end
@@ -181,11 +185,11 @@
     end
 
     def menuitem_top_enabled?()
-      @page.name != FRONT_PAGE_NAME
+      @page.title != FRONT_PAGE_NAME
     end
 
     def menuitem_help_enabled?()
-      @page.name != HELP_PAGE_NAME
+      @page.title != HELP_PAGE_NAME
     end
 
     def diff_base_revision
@@ -411,6 +415,10 @@
       'preview'
     end
 
+    def page_title
+      @page.syntax.title(@text) || page_name
+    end
+
     def diff_base_revision
       @original_revision || @page.revision || 0
     end
@@ -468,7 +476,7 @@
     end
 
     def page_list
-      @repository.page_names.sort_by {|name| name.downcase }
+      @repository.page_names_and_titles.sort_by {|name, title| title.downcase }
     end
 
     def orphan_page?(name)
@@ -527,7 +535,7 @@
 
     def matched_pages(&block)
       title_match, not_match = *@repository.pages\
-          .partition {|page| @patterns.all? {|re| re =~ page.name } }
+          .partition {|page| @patterns.all? {|re| re =~ page.title } }
       title_match\
           .sort_by {|page| -page.mtime.to_i }.each(&block)
       not_match\
Index: template/revlinks.rhtml
===================================================================
--- template/revlinks.rhtml	(.../vendor)	(revision 58)
+++ template/revlinks.rhtml	(.../branches/wiki)	(revision 58)
@@ -3,7 +3,7 @@
 <%
     show_max = 10
     ordered_revlinks()[0, show_max].each do |page|
-%><a href="<%= view_url(page.name) %>"><%= escape_html(page.name) %></a>
+%><a href="<%= view_url(page.name) %>"><%= escape_html(page.repository.name2title(page.name)) %></a>
 <%
     end
 %>
Index: template/history.rhtml
===================================================================
--- template/history.rhtml	(.../vendor)	(revision 58)
+++ template/history.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,13 +5,13 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> : History</title>
+  <title><%= page_title() %> : History</title>
 </head>
 <body>
 
 .include menu
 
-<h1><%= logo_url() %><%= page_name() %> : History</h1>
+<h1><%= logo_url() %><%= page_title() %> : History</h1>
 <ul>
 <%
     logs().sort_by {|log| -log.revision }.each do |log| %>
Index: template/search_result.rhtml
===================================================================
--- template/search_result.rhtml	(.../vendor)	(revision 58)
+++ template/search_result.rhtml	(.../branches/wiki)	(revision 58)
@@ -19,7 +19,7 @@
     n_hits = 0
     matched_pages do |page|
 %>
-<li><a href="<%= view_url(page.name) %>"><%= escape_html(page.name) %></a> <%= escape_html(shorten(page.source)) %></li>
+<li><a href="<%= view_url(page.name) %>"><%= escape_html(page.title) %></a> <%= escape_html(shorten(page.source)) %></li>
 <%
       n_hits += 1
     end
Index: template/diff.rhtml
===================================================================
--- template/diff.rhtml	(.../vendor)	(revision 58)
+++ template/diff.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,13 +5,13 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> : Diff</title>
+  <title><%= page_title() %> : Diff</title>
 </head>
 <body>
 
 .include menu
 
-<h1><%= logo_url() %><%= page_name() %> : Diff rev <%= rev1() %> &lt;=&gt; rev <%= rev2() %></h1>
+<h1><%= logo_url() %><%= page_title() %> : Diff rev <%= rev1() %> &lt;=&gt; rev <%= rev2() %></h1>
 
 <p class="revnavi">
 [<%  if rev1() > 1  %>
Index: template/annotate.rhtml
===================================================================
--- template/annotate.rhtml	(.../vendor)	(revision 58)
+++ template/annotate.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,13 +5,13 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> : Annotate</title>
+  <title><%= page_title() %> : Annotate</title>
 </head>
 <body>
 
 .include menu
 
-<h1><%= logo_url() %><%= page_name() %> : Annotate<%= (revision() ? " rev #{revision()}" : '') %></h1>
+<h1><%= logo_url() %><%= page_title() %> : Annotate<%= (revision() ? " rev #{revision()}" : '') %></h1>
 
 <p class="revnavi">[<a href="<%= page_view_url() %>">&gt;&gt;HEAD</a>]</p>
 
Index: template/view.rhtml
===================================================================
--- template/view.rhtml	(.../vendor)	(revision 58)
+++ template/view.rhtml	(.../branches/wiki)	(revision 58)
@@ -4,13 +4,13 @@
   <meta http-equiv="Content-Type" content="text/html; charset=<%= page_charset() %>">
   <meta http-equiv="Content-Language" content="ja-JP">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= front_page?() ? site_name() : page_name() %></title>
+  <title><%= front_page?() ? site_name() : page_title() %></title>
 </head>
 <body>
 
 .include menu
 
-<h1><%= logo_url() %><%= front_page?() ? site_name() : page_name() %></h1>
+<h1><%= logo_url() %><%= front_page?() ? site_name() : page_title() %></h1>
 <p>
 <span class="lastmodified"><%= format_time(last_modified()) %></span>
 (<%= times_before(last_modified()) %>);
Index: template/list.rhtml
===================================================================
--- template/list.rhtml	(.../vendor)	(revision 58)
+++ template/list.rhtml	(.../branches/wiki)	(revision 58)
@@ -13,9 +13,9 @@
 
 <h1><%= logo_url() %>List of All Pages</h1>
 <ol>
-<%  page_list().each do |page|  %>
-<li><a href="<%= view_url(page) %>"><%= escape_html(page) %></a>
-    <%  if orphan_page?(page)  %>[orphan]<%  end  %></li>
+<%  page_list().each do |name, title|  %>
+<li><a href="<%= view_url(name) %>"><%= escape_html(title) %></a>
+    <%  if orphan_page?(name)  %>[orphan]<%  end  %></li>
 <%  end  %>
 </ol>
 
Index: template/gdiff.rhtml
===================================================================
--- template/gdiff.rhtml	(.../vendor)	(revision 58)
+++ template/gdiff.rhtml	(.../branches/wiki)	(revision 58)
@@ -17,7 +17,7 @@
 <p><a href="<%= cgi_url() %>?cmd=gdiff;org=cookie">[Diff from my last visit]</a></p>
 
 <% diffs().each do |log| %>
-<h2><a href="<%= view_url(log.page_name) %>"><%= escape_html(log.page_name) %></a></h2>
+<h2><a href="<%= view_url(log.page_name) %>"><%= escape_html(@repository.name2title(log.page_name)) %></a></h2>
 <p class="minimenu">
 <a href="<%= cgi_url() %>?cmd=edit;name=<%= escape_html(log.page_name) %>">[edit]</a>
 <% if log.rev2 > 1 %>
Index: template/recent.rhtml
===================================================================
--- template/recent.rhtml	(.../vendor)	(revision 58)
+++ template/recent.rhtml	(.../branches/wiki)	(revision 58)
@@ -15,7 +15,7 @@
 <p><a href="<%= cgi_url() %>?cmd=gdiff;org=cookie">[Diff from my last visit]</a></p>
 <ol>
 <%  page_list().each do |page|  %>
-<li><%= format_time(page.mtime) %>: <a href="<%= view_url(page.name) %>"><%= escape_html(page.name) %></a></li>
+<li><%= format_time(page.mtime) %>: <a href="<%= view_url(page.name) %>"><%= escape_html(page.title) %></a></li>
 <%  end  %>
 </ol>
 
Index: template/preview.rhtml
===================================================================
--- template/preview.rhtml	(.../vendor)	(revision 58)
+++ template/preview.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,7 +5,7 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> : Preview</title>
+  <title><%= page_title() %> : Preview</title>
 </head>
 <body>
 
@@ -15,7 +15,7 @@
 <span class="previewmark">Preview</span>
 </p>
 
-<h1><%= logo_url() %><%= page_name() %></h1>
+<h1><%= logo_url() %><%= page_title() %></h1>
 
 <%= compiled_body() %>
 
Index: template/edit.rhtml
===================================================================
--- template/edit.rhtml	(.../vendor)	(revision 58)
+++ template/edit.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,7 +5,7 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> : Edit</title>
+  <title><%= page_title() %> : Edit</title>
 </head>
 <body>
 
Index: template/viewrev.rhtml
===================================================================
--- template/viewrev.rhtml	(.../vendor)	(revision 58)
+++ template/viewrev.rhtml	(.../branches/wiki)	(revision 58)
@@ -5,13 +5,13 @@
   <meta http-equiv="Content-Language" content="ja-JP">
   <meta name="robots" content="noindex,nofollow">
   <link rel="stylesheet" type="text/css" href="<%= css_url() %>">
-  <title><%= page_name() %> rev 1.<%= revision() %></title>
+  <title><%= page_title() %> rev 1.<%= revision() %></title>
 </head>
 <body>
 
 .include menu
 
-<h1><%= logo_url() %><%= page_name() %> rev <%= revision() %></h1>
+<h1><%= logo_url() %><%= page_title() %> rev <%= revision() %></h1>
 
 <p><span class="lastmodified">Saved at: <%= format_time(last_modified()) %></span></p>
 
