From 8ed2bd45ef8c2f56e124f478da384fcad423dbf0 Mon Sep 17 00:00:00 2001
From: Shengyu Zhang <i@silverrainz.me>
Date: Sun, 20 Oct 2024 19:55:59 +0800
Subject: [PATCH] update

---
 src/sphinxnotes/snippet/cli.py                | 11 ++++-
 src/sphinxnotes/snippet/ext.py                |  2 +-
 .../snippet/integration/binding.sh            |  2 +-
 src/sphinxnotes/snippet/snippets.py           | 48 ++++++++++++-------
 4 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/src/sphinxnotes/snippet/cli.py b/src/sphinxnotes/snippet/cli.py
index 7310222..a52b17a 100644
--- a/src/sphinxnotes/snippet/cli.py
+++ b/src/sphinxnotes/snippet/cli.py
@@ -140,7 +140,12 @@ def main(argv: list[str] = sys.argv[1:]):
         '--text',
         '-t',
         action='store_true',
-        help='get source reStructuredText of snippet',
+        help='get text representation of snippet',
+    )
+    getparser.add_argument(
+        '--src',
+        action='store_true',
+        help='get source text of snippet',
     )
     getparser.add_argument(
         '--url',
@@ -273,7 +278,9 @@ def p(*args, **opts):
             p('no such index ID', file=sys.stderr)
             sys.exit(1)
         if args.text:
-            p('\n'.join(item.snippet.rst))
+            p('\n'.join(item.snippet.text))
+        if args.src:
+            p('\n'.join(item.snippet.source))
         if args.docname:
             p(item.snippet.docname)
         if args.file:
diff --git a/src/sphinxnotes/snippet/ext.py b/src/sphinxnotes/snippet/ext.py
index 2f216ba..c30f55e 100644
--- a/src/sphinxnotes/snippet/ext.py
+++ b/src/sphinxnotes/snippet/ext.py
@@ -56,7 +56,7 @@ def extract_excerpt(s: Snippet) -> str:
     elif isinstance(s, Section) and s.title is not None:
         return '[' + s.title + ']'
     elif isinstance(s, Code):
-        return s.lang + '`' + s.desc + '`'
+        return '`' + (s.lang + ':').ljust(8, ' ') + ' ' + s.desc + '`'
     return ''
 
 
diff --git a/src/sphinxnotes/snippet/integration/binding.sh b/src/sphinxnotes/snippet/integration/binding.sh
index 474b382..966da21 100644
--- a/src/sphinxnotes/snippet/integration/binding.sh
+++ b/src/sphinxnotes/snippet/integration/binding.sh
@@ -6,7 +6,7 @@
 # :Version: 20240828
 
 function snippet_view() {
-  selection=$(snippet_list --tags ds)
+  selection=$(snippet_list --tags c)
   [ -z "$selection" ] && return
 
   # Make sure we have $PAGER
diff --git a/src/sphinxnotes/snippet/snippets.py b/src/sphinxnotes/snippet/snippets.py
index 8f634cb..ac920c0 100644
--- a/src/sphinxnotes/snippet/snippets.py
+++ b/src/sphinxnotes/snippet/snippets.py
@@ -38,15 +38,20 @@ class Snippet(object):
     # :rst:role:`doc`.
     docname: str
 
-    #: Absolute path of the source file.
+    #: Absolute path to the source file.
     file: str
 
-    #: Line number range of snippet, in the source file which is left closed
-    #: and right opened.
+    #: Line number range of source file (:attr:`Snippet.file`),
+    #: left closed and right opened.
     lineno: tuple[int, int]
 
-    #: The original reStructuredText of snippet
-    rst: list[str]
+    #: The source text read from source file (:attr:`Snippet.file`),
+    # in Markdown or reStructuredText.
+    source: list[str]
+
+    #: Text representation of the snippet, usually generated form
+    # :meth:`nodes.Element.astext`.
+    text: list[str]
 
     #: The possible identifier key of snippet, which is picked from nodes'
     #: (or nodes' parent's) `ids attr`_.
@@ -57,7 +62,7 @@ class Snippet(object):
     def __init__(self, *nodes: nodes.Element) -> None:
         assert len(nodes) != 0
 
-        env: BuildEnvironment = nodes[0].document.settings.env
+        env: BuildEnvironment = nodes[0].document.settings.env  # type: ignore
 
         file, docname = None, None
         for node in nodes:
@@ -66,7 +71,7 @@ def __init__(self, *nodes: nodes.Element) -> None:
                 docname = env.path2doc(file)
                 break
         if not file or not docname:
-            raise ValueError('Missing source file or docname')
+            raise ValueError(f'Nodes {nodes} lacks source file or docname')
         self.file = file
         self.docname = docname
 
@@ -78,13 +83,18 @@ def __init__(self, *nodes: nodes.Element) -> None:
             lineno[1] = max(lineno[1], _line_of_end(node))
         self.lineno = (lineno[0], lineno[1])
 
-        lines = []
+        source = []
         with open(self.file, 'r') as f:
             start = self.lineno[0] - 1
             stop = self.lineno[1] - 1
             for line in itertools.islice(f, start, stop):
-                lines.append(line.strip('\n'))
-        self.rst = lines
+                source.append(line.strip('\n'))
+        self.source = source
+
+        text = []
+        for node in nodes:
+            text.extend(node.astext().split('\n'))
+        self.text = text
 
         # Find exactly one ID attr in nodes
         self.refid = None
@@ -102,19 +112,21 @@ def __init__(self, *nodes: nodes.Element) -> None:
 
 
 class Code(Snippet):
+    ALLOWED_LANGS = ['console']
     #: Language of code block
     lang: str
     #: Description of code block, usually the text of preceding paragraph
     desc: str
-    #: The code itself.
-    code: str
 
     def __init__(self, node: nodes.literal_block) -> None:
         assert isinstance(node, nodes.literal_block)
         super().__init__(node)
 
         self.lang = node['language']
-        self.code = node.astext()
+        if self.lang not in self.ALLOWED_LANGS:
+            raise ValueError(
+                f'Language of node {node} {self.lang} not in allowed language list {self.ALLOWED_LANGS}',
+            )
 
         self.desc = ''
         if isinstance(para := node.previous_sibling(), nodes.paragraph):
@@ -131,7 +143,10 @@ def __init__(self, node: nodes.literal_block) -> None:
             # In this case, the preceding paragraph "Foo:" is the descritpion
             # of the code block. This convention also applies to the code,
             # code-block, sourcecode directive.
-            self.desc += para.astext().replace('\n', ' ')
+
+            # For better display, the trailing colon is removed.
+            # TODO: https://en.wikipedia.org/wiki/Colon_(punctuation)#Computing
+            self.desc += para.astext().replace('\n', ' ').rstrip(':::︁︓﹕')
         if caption := node.get('caption'):
             # Use caption as descritpion.
             # In sphinx, all of code-block, sourcecode and code have caption option.
@@ -147,10 +162,9 @@ class WithTitle(object):
     title: str
 
     def __init__(self, node: nodes.Element) -> None:
-        if title := node.next_node(nodes.title):
-            self.title = title.astext()
-        else:
+        if not (title := node.next_node(nodes.title)):
             raise ValueError(f'Node f{node} lacks title')
+        self.title = title.astext()
 
 
 class Section(Snippet, WithTitle):