├── tests
├── __init__.py
├── roots
│ ├── test-default
│ │ ├── conf.py
│ │ ├── _static
│ │ │ └── pyconjp2019.jpg
│ │ └── index.rst
│ ├── test-roles
│ │ ├── conf.py
│ │ └── index.rst
│ ├── test-enable-referrer
│ │ ├── conf.py
│ │ └── index.rst
│ ├── test-external-link-icon
│ │ ├── conf.py
│ │ ├── _static
│ │ │ ├── breakfast.jpg
│ │ │ └── pyconjp2019.jpg
│ │ └── index.rst
│ ├── test-method-assignment
│ │ ├── index.rst
│ │ ├── conf.py
│ │ └── extensions
│ │ │ └── custom_translator.py
│ └── test-dynamic-class
│ │ ├── index.rst
│ │ ├── conf.py
│ │ └── extensions
│ │ └── custom_translator.py
├── test_custom_translator.py
├── test_enable_referrer.py
├── conftest.py
├── test_roles.py
├── test_show_external_link_icon.py
├── helpers.py
└── test_sphinx_new_tab_link.py
├── setup.py
├── docs
├── api.rst
├── _static
│ ├── breakfast.jpg
│ └── pyconjp2019.jpg
├── Makefile
├── index.rst
├── make.bat
├── conf.py
├── markdown-example.md
├── guide.rst
└── guide.en.rst
├── src
└── sphinx_new_tab_link
│ ├── core.py
│ ├── extras.py
│ ├── roles.py
│ └── __init__.py
├── .github
└── workflows
│ ├── publish.yml
│ ├── docs.yml
│ └── testing.yml
├── LICENSE
├── CLAUDE.md
├── README.md
├── .gitignore
└── pyproject.toml
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup()
4 |
--------------------------------------------------------------------------------
/tests/roots/test-default/conf.py:
--------------------------------------------------------------------------------
1 | extensions = [
2 | "sphinx_new_tab_link",
3 | ]
4 |
--------------------------------------------------------------------------------
/tests/roots/test-roles/conf.py:
--------------------------------------------------------------------------------
1 | extensions = [
2 | "sphinx_new_tab_link",
3 | ]
4 |
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | API
2 | ===
3 |
4 | .. automodule:: sphinx_new_tab_link
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/_static/breakfast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftnext/sphinx-new-tab-link/HEAD/docs/_static/breakfast.jpg
--------------------------------------------------------------------------------
/docs/_static/pyconjp2019.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftnext/sphinx-new-tab-link/HEAD/docs/_static/pyconjp2019.jpg
--------------------------------------------------------------------------------
/tests/roots/test-enable-referrer/conf.py:
--------------------------------------------------------------------------------
1 | extensions = [
2 | "sphinx_new_tab_link",
3 | ]
4 |
5 | new_tab_link_enable_referrer = True
6 |
--------------------------------------------------------------------------------
/tests/roots/test-external-link-icon/conf.py:
--------------------------------------------------------------------------------
1 | extensions = [
2 | "sphinx_new_tab_link",
3 | ]
4 |
5 | new_tab_link_show_external_link_icon = True
6 |
--------------------------------------------------------------------------------
/tests/roots/test-default/_static/pyconjp2019.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftnext/sphinx-new-tab-link/HEAD/tests/roots/test-default/_static/pyconjp2019.jpg
--------------------------------------------------------------------------------
/tests/roots/test-enable-referrer/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Test Referrer Mode
3 | ==================
4 |
5 | Anonymous external link `httpbin `__.
--------------------------------------------------------------------------------
/tests/roots/test-external-link-icon/_static/breakfast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftnext/sphinx-new-tab-link/HEAD/tests/roots/test-external-link-icon/_static/breakfast.jpg
--------------------------------------------------------------------------------
/tests/roots/test-external-link-icon/_static/pyconjp2019.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ftnext/sphinx-new-tab-link/HEAD/tests/roots/test-external-link-icon/_static/pyconjp2019.jpg
--------------------------------------------------------------------------------
/tests/roots/test-method-assignment/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Test documentation
3 | ==================
4 |
5 | Support custom extension.
6 |
7 | https://ftnext.github.io/sphinx-new-tab-link/guide.html
8 |
--------------------------------------------------------------------------------
/tests/roots/test-dynamic-class/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Test documentation
3 | ==================
4 |
5 | Support custom with mixin.
6 |
7 | https://millionlive-theaterdays.idolmaster-official.jp/idol/emily/
8 |
--------------------------------------------------------------------------------
/tests/roots/test-dynamic-class/conf.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | sys.path.insert(0, os.path.abspath("./extensions"))
5 |
6 | extensions = [
7 | "sphinx_new_tab_link",
8 | "custom_translator",
9 | ]
10 |
--------------------------------------------------------------------------------
/tests/roots/test-method-assignment/conf.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | sys.path.insert(0, os.path.abspath("./extensions"))
5 |
6 | extensions = [
7 | "custom_translator",
8 | "sphinx_new_tab_link",
9 | ]
10 |
--------------------------------------------------------------------------------
/src/sphinx_new_tab_link/core.py:
--------------------------------------------------------------------------------
1 | from docutils import nodes
2 |
3 | from sphinx_new_tab_link.extras import external_link_icon_html
4 |
5 |
6 | def add_icon_to_reference(node: nodes.reference) -> nodes.reference:
7 | node.append(nodes.Text(" "))
8 | node.append(nodes.raw(text=external_link_icon_html(), format="html"))
9 | return node
10 |
--------------------------------------------------------------------------------
/tests/roots/test-roles/index.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Role test
3 | =========
4 |
5 | .. _ref_to_section1:
6 |
7 | Section 1
8 | =========
9 |
10 | External link (without icon): `Example `__
11 |
12 | External link with icon: :icon-link:`httpbin `
13 |
14 | Section 2
15 | =========
16 |
17 | Internal link: :icon-link:`ref_to_section1`
18 |
--------------------------------------------------------------------------------
/tests/roots/test-external-link-icon/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Test documentation
3 | ==================
4 |
5 | URL https://pypi.org/project/sphinx-new-tab-link/ inline.
6 |
7 | .. image:: _static/pyconjp2019.jpg
8 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
9 |
10 | .. figure:: _static/breakfast.jpg
11 | :target: https://www.flickr.com/photos/pyconjp/48818171768/in/album-72157710870622516/
12 |
--------------------------------------------------------------------------------
/tests/roots/test-method-assignment/extensions/custom_translator.py:
--------------------------------------------------------------------------------
1 | """There is no need of method assignment.
2 |
3 | v0.3.0 or later, sphinx_new_tab_link supports dynamic inheritance to add
4 | functionality to open an external link in a new tab of the browser.
5 | """
6 |
7 | from sphinx.writers.html5 import HTML5Translator
8 |
9 |
10 | class MyTranslator(HTML5Translator): ... # NOQA: E701
11 |
12 |
13 | # MyTranslator.starttag = NewTabLinkHTMLTranslator.starttag
14 |
15 |
16 | def setup(app):
17 | app.set_translator("html", MyTranslator)
18 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Python Package
2 |
3 | on:
4 | release:
5 | types:
6 | - published
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | publish:
13 | runs-on: ubuntu-latest
14 | environment: pypi
15 | permissions:
16 | id-token: write
17 |
18 | steps:
19 | - uses: actions/checkout@v5
20 | - name: Build package
21 | run: |
22 | pipx run build
23 | pipx run twine check dist/*
24 | - name: Publish package
25 | uses: pypa/gh-action-pypi-publish@release/v1
26 |
--------------------------------------------------------------------------------
/tests/roots/test-dynamic-class/extensions/custom_translator.py:
--------------------------------------------------------------------------------
1 | # https://github.com/ftnext/sphinx-new-tab-link/issues/11
2 | import types
3 |
4 | from sphinx.writers.html import HTMLTranslator
5 |
6 |
7 | class StartTagMixin:
8 | def starttag(self, *args, **kwargs):
9 | return super().starttag(*args, **kwargs)
10 |
11 |
12 | def setup(app):
13 | app.set_translator("html", HTMLTranslator)
14 |
15 | translator_class = types.new_class(
16 | "MyTranslator",
17 | (StartTagMixin, app.registry.translators["html"]),
18 | {},
19 | )
20 | app.set_translator("html", translator_class, override=True)
21 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/tests/test_custom_translator.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | from .helpers import assert_referrer_disabled_reference, extract_references
8 |
9 | if TYPE_CHECKING:
10 | from pathlib import Path
11 |
12 |
13 | @pytest.fixture
14 | def builder() -> str:
15 | return "html"
16 |
17 |
18 | @pytest.fixture(params=["method-assignment", "dynamic-class"])
19 | def directory_name(request) -> str:
20 | return request.param
21 |
22 |
23 | def test_should_open_new_tab(built_html_path: Path) -> None:
24 | references = extract_references(built_html_path)
25 |
26 | assert len(references) == 1
27 | assert_referrer_disabled_reference(references[0])
28 |
--------------------------------------------------------------------------------
/tests/test_enable_referrer.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | from .helpers import (
8 | assert_reference_is_external_with_referrer,
9 | extract_references,
10 | )
11 |
12 | if TYPE_CHECKING:
13 | from pathlib import Path
14 |
15 |
16 | @pytest.fixture
17 | def builder() -> str:
18 | return "html"
19 |
20 |
21 | @pytest.fixture
22 | def directory_name() -> str:
23 | return "enable-referrer"
24 |
25 |
26 | def test_should_enable_referrer_when_configured(built_html_path: Path) -> None:
27 | references = extract_references(built_html_path)
28 |
29 | assert_reference_is_external_with_referrer(
30 | references[0], "https://httpbin.org/"
31 | )
32 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. sphinx-new-tab-link documentation documentation master file, created by
2 | sphinx-quickstart on Wed Nov 16 23:12:14 2022.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to sphinx-new-tab-link documentation's documentation!
7 | =============================================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: For library users:
12 |
13 | guide.rst
14 | guide.en.rst
15 | markdown-example
16 |
17 | .. toctree::
18 | :maxdepth: 1
19 | :caption: For developers:
20 |
21 | api.rst
22 |
23 | Indices and tables
24 | ==================
25 |
26 | * :ref:`genindex`
27 | * :ref:`modindex`
28 | * :ref:`search`
29 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Build document
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | docs:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v5
14 | - name: Set up Python
15 | uses: actions/setup-python@v6
16 | with:
17 | python-version: '3.13'
18 | - name: Install build dependencies
19 | run: |
20 | python -m pip install -U pip
21 | python -m pip install . myst-parser
22 | - name: Build document
23 | run: |
24 | cd docs/
25 | make html
26 | - name: Push to GitHub Pages branch
27 | uses: ftnext/action-push-ghpages@v1.0.0
28 | with:
29 | build_dir: docs/_build/html
30 | github_token: ${{ secrets.GITHUB_TOKEN }}
31 |
--------------------------------------------------------------------------------
/.github/workflows/testing.yml:
--------------------------------------------------------------------------------
1 | name: Run tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 | schedule:
11 | - cron: '0 9 * * 1'
12 |
13 | jobs:
14 | tests:
15 | runs-on: ubuntu-latest
16 | strategy:
17 | fail-fast: true
18 | matrix:
19 | python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
20 |
21 | steps:
22 | - uses: actions/checkout@v5
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v6
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | - name: Install dependencies
28 | run: |
29 | python -m pip install -U pip
30 | python -m pip install .[dev,testing,typecheck,lint]
31 | - name: Run tests
32 | run: |
33 | task test
34 |
--------------------------------------------------------------------------------
/src/sphinx_new_tab_link/extras.py:
--------------------------------------------------------------------------------
1 | def external_link_icon_html() -> str:
2 | # https://primer.style/foundations/icons/link-external-16/
3 | return (
4 | # https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L21-L26
5 | '"
8 | )
9 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/src/sphinx_new_tab_link/roles.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from urllib.parse import urlparse
4 |
5 | from docutils import nodes
6 | from sphinx.util.docutils import ReferenceRole
7 |
8 | from sphinx_new_tab_link.core import add_icon_to_reference
9 |
10 |
11 | class IconLinkRole(ReferenceRole):
12 | """Role to create a external link with an icon.
13 |
14 | Of course, opened in new tabs of your browser.
15 |
16 | Usage::
17 |
18 | :icon-link:`title `
19 | """
20 |
21 | def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]:
22 | parse_result = urlparse(self.target)
23 | if parse_result.scheme:
24 | node = nodes.reference(
25 | text=self.title, refuri=self.target, internal=False
26 | )
27 | reference_with_icon = add_icon_to_reference(node)
28 | return [reference_with_icon], []
29 | else:
30 | node = nodes.reference(
31 | text=self.title, refuri=f"#{self.target}", internal=True
32 | )
33 | return [node], []
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 nikkie
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 |
5 | from .helpers import prepare_files
6 |
7 | pytest_plugins = "sphinx.testing.fixtures"
8 | collect_ignore = ["roots"]
9 |
10 |
11 | @pytest.fixture(scope="session")
12 | def rootdir() -> Path:
13 | return Path(__file__).parent / "roots"
14 |
15 |
16 | @pytest.fixture
17 | def builder() -> str:
18 | raise NotImplementedError("Define `builder` fixture in each test module")
19 |
20 |
21 | @pytest.fixture
22 | def directory_name() -> str:
23 | raise NotImplementedError(
24 | "Define `directory_name` fixture in each test module"
25 | )
26 |
27 |
28 | @pytest.fixture
29 | def prepared_srcdir(
30 | sphinx_test_tempdir: Path, rootdir: Path, directory_name: str
31 | ) -> Path:
32 | srcdir = sphinx_test_tempdir / directory_name
33 | prepare_files(rootdir / f"test-{directory_name}", srcdir)
34 |
35 | return srcdir
36 |
37 |
38 | @pytest.fixture
39 | def built_html_path(make_app, builder: str, prepared_srcdir: Path) -> Path:
40 | app = make_app(builder, srcdir=prepared_srcdir)
41 | app.build()
42 |
43 | return app.outdir / "index.html"
44 |
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # Development Guide for sphinx-new-tab-link
2 |
3 | ## Build & Test Commands
4 | - Format code: `task format`
5 | - Run tests: `task test` (includes pre/post hooks for format and check)
6 | - Lint & type check: `task check`
7 | - Run single test: `pytest tests/test_file.py::test_function -v`
8 | - Complete workflow: `task format && task test && task check`
9 |
10 | ## Environment Setup
11 | - Project uses Python virtual environment (venv directory)
12 | - Claude Code and other AI tools should ignore the venv directory
13 | - Use Python 3.10 or higher for development
14 |
15 | ## Code Style Guidelines
16 | - Line length: 79 characters
17 | - Formatting: Black with black profile for isort
18 | - Type annotations required, use `typing.TYPE_CHECKING` for annotation imports
19 | - Import order: stdlib → third-party → local modules (enforced by isort)
20 | - Naming: lowercase_with_underscores for variables, functions, methods
21 |
22 | ## Error Handling
23 | - Use specific exceptions with clear error messages
24 | - Test expected exceptions explicitly
25 |
26 | ## Development Workflow
27 | - Write tests before implementation
28 | - Ensure all tests pass before committing
29 | - CI runs tests on Python 3.10-3.14
30 |
--------------------------------------------------------------------------------
/tests/test_roles.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | from .helpers import (
8 | assert_reference_is_external,
9 | assert_reference_is_external_with_icon,
10 | assert_reference_is_not_external,
11 | extract_references,
12 | )
13 |
14 | if TYPE_CHECKING:
15 | from pathlib import Path
16 |
17 |
18 | @pytest.fixture
19 | def builder() -> str:
20 | return "html"
21 |
22 |
23 | @pytest.fixture
24 | def directory_name() -> str:
25 | return "roles"
26 |
27 |
28 | def test_see_external_link_with_icon(built_html_path: Path) -> None:
29 | references = extract_references(built_html_path)
30 |
31 | ref = references[1]
32 | assert_reference_is_external_with_icon(ref, "https://httpbin.org/")
33 | assert ref.text == "httpbin "
34 |
35 |
36 | def test_see_internal_link_without_icon(built_html_path: Path) -> None:
37 | references = extract_references(built_html_path)
38 |
39 | ref = references[2]
40 | assert_reference_is_not_external(ref)
41 | assert not ref.svg
42 |
43 |
44 | def test_regression_1(built_html_path: Path) -> None:
45 | references = extract_references(built_html_path)
46 |
47 | ref = references[0]
48 | assert_reference_is_external(ref, "https://example.com/")
49 |
--------------------------------------------------------------------------------
/tests/roots/test-default/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Test documentation
3 | ==================
4 |
5 | reST supports a lot of link notations.
6 |
7 | Raw
8 | ===
9 |
10 | http://abehiroshi.la.coocan.jp
11 |
12 | - 🎀
13 | - 👑
14 | - 💧
15 | - 👠
16 | - 🤲
17 |
18 | We can write URL https://pypi.org/project/sphinx-new-tab-link/ inline.
19 |
20 | .. _ref_to_section1:
21 |
22 | Section 1
23 | =========
24 |
25 | - 🐑
26 | - 🎤
27 | - 🍞
28 | - 📶
29 | - 🖤
30 |
31 | Markup
32 | ======
33 |
34 | .. _reStructuredText Primer: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
35 |
36 | Refer target `reStructuredText Primer`_ 😃
37 |
38 | Embedded URL
39 | ------------
40 |
41 | External link (with target definition): `Example `_
42 |
43 | Anonymous external link `httpbin `__.
44 |
45 | Here `Example`_, we can refer the target!
46 |
47 | Section 2
48 | =========
49 |
50 | .. __: https://github.com/ftnext/sphinx-new-tab-link
51 |
52 | This package's `source code`__.
53 |
54 | 1. ヒ
55 | 2. ト
56 | 3. リ
57 | 4. ダ
58 | 5. ケ
59 | 6. ナ
60 | 7. ン
61 | 8. テ
62 | 9. エ
63 | 10. ラ
64 |
65 | .. image:: _static/pyconjp2019.jpg
66 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
67 |
68 | This is internal link: :ref:`ref_to_section1`
69 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | import os
7 | import sys
8 |
9 | sys.path.insert(0, os.path.abspath("../src/sphinx_new_tab_link"))
10 |
11 | # -- Project information -----------------------------------------------------
12 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
13 |
14 | project = "sphinx-new-tab-link documentation"
15 | copyright = "2022, nikkie"
16 | author = "nikkie"
17 |
18 | # -- General configuration ---------------------------------------------------
19 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
20 |
21 | extensions = [
22 | "myst_parser",
23 | "sphinx.ext.autodoc",
24 | "sphinx_new_tab_link",
25 | "sphinx.ext.githubpages",
26 | ]
27 |
28 | templates_path = ["_templates"]
29 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
30 |
31 | language = "ja"
32 |
33 | # -- Options for HTML output -------------------------------------------------
34 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
35 |
36 | html_theme = "alabaster"
37 | html_static_path = ["_static"]
38 |
39 |
40 | # -- Options for MyST-Parser -------------------------------------------------
41 | # https://myst-parser.readthedocs.io/en/latest/configuration.html
42 |
43 | myst_heading_anchors = 2
44 |
--------------------------------------------------------------------------------
/tests/test_show_external_link_icon.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | from .helpers import assert_reference_is_external_with_icon, extract_references
8 |
9 | if TYPE_CHECKING:
10 | from pathlib import Path
11 |
12 |
13 | @pytest.fixture
14 | def builder() -> str:
15 | return "html"
16 |
17 |
18 | @pytest.fixture
19 | def directory_name() -> str:
20 | return "external-link-icon"
21 |
22 |
23 | def test_see_external_link_icon(built_html_path: Path) -> None:
24 | references = extract_references(built_html_path)
25 |
26 | ref = references[0]
27 | assert ref.text == "https://pypi.org/project/sphinx-new-tab-link/ "
28 | assert_reference_is_external_with_icon(
29 | ref, "https://pypi.org/project/sphinx-new-tab-link/"
30 | )
31 |
32 |
33 | def test_can_see_icon_with_image_directive_target(
34 | built_html_path: Path,
35 | ) -> None:
36 | # https://github.com/ftnext/sphinx-new-tab-link/issues/16
37 | references = extract_references(built_html_path)
38 |
39 | ref = references[1]
40 | assert_reference_is_external_with_icon(
41 | ref,
42 | "https://www.flickr.com/photos/pyconjp/48743997848/"
43 | "in/album-72157710870622516/",
44 | )
45 | assert ref.img
46 |
47 |
48 | def test_can_see_icon_with_figure_directive_target(
49 | built_html_path: Path,
50 | ) -> None:
51 | references = extract_references(built_html_path)
52 |
53 | ref = references[2]
54 | assert_reference_is_external_with_icon(
55 | ref,
56 | "https://www.flickr.com/photos/pyconjp/48818171768/"
57 | "in/album-72157710870622516/",
58 | )
59 | assert ref.img
60 |
--------------------------------------------------------------------------------
/docs/markdown-example.md:
--------------------------------------------------------------------------------
1 | # Markdown example / Markdownの例
2 |
3 | *Here is an example of how `sphinx-new-tab-link` works with HTML built from a Markdown file.*
4 | *MarkdownファイルからビルドされたHTMLでも`sphinx-new-tab-link`が機能する例です。*
5 |
6 | ```{note} reST version: [reST example](guide.en.rst) / [reSTの例](guide.rst)
7 | ```
8 |
9 | ## Links / リンク集
10 |
11 | * PyPI
12 | * [GitHub Repository](https://github.com/ftnext/sphinx-new-tab-link)
13 |
14 | ## Installation / インストール
15 |
16 | ```shell
17 | $ pip install sphinx-new-tab-link myst-parser
18 | ```
19 |
20 | ## How to use / 使い方
21 |
22 | `conf.py`
23 |
24 | ```python
25 | extensions = [
26 | "myst_parser",
27 | "sphinx_new_tab_link",
28 | ]
29 | ```
30 |
31 | ## Supported notations / 対応している記法
32 |
33 | It supports various notations of a external link possible in MyST.
34 | MySTで可能なさまざまな記法による外部リンクをサポートしています。
35 |
36 | ref:
37 |
38 | ✅Autolinks
39 |
40 | See *PyPI* at ['Links / リンク集' section](#links--リンク集)
41 |
42 | ```md
43 |
44 | ```
45 |
46 | ✅Inline links (to external target)
47 |
48 | See *GitHub Repository* at ['Links / リンク集' section](#links--リンク集)
49 |
50 | ```md
51 | [GitHub Repository](https://github.com/ftnext/sphinx-new-tab-link)
52 | ```
53 |
54 | ✅Reference links (to external target)
55 |
56 | [公開版ガイド(ja)][guide-ja]
57 |
58 | [guide-ja]: https://ftnext.github.io/sphinx-new-tab-link/guide.html
59 |
60 | ```md
61 | [公開版ガイド(ja)][guide-ja]
62 |
63 | [guide-ja]: https://ftnext.github.io/sphinx-new-tab-link/guide.html
64 | ```
65 |
66 | Enjoy documentation!🙌
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sphinx-new-tab-link
2 | 
3 | [](https://badge.fury.io/py/sphinx-new-tab-link)
4 | [](https://pypi.org/project/sphinx-new-tab-link/)
5 |
6 | Open external links in new tabs of the browser in Sphinx HTML documents
7 |
8 | ## Overview
9 |
10 | If you enable `sphinx_new_tab_link`, external links of built HTML are opened in new tabs of your browser.
11 |
12 | The reST
13 |
14 | ```rst
15 | External link: `Example `_
16 | ```
17 |
18 | is converted into
19 |
20 | ```html
21 | External link: Example
22 | ```
23 |
24 | ## Usage
25 |
26 | First, create your Sphinx documentation.
27 |
28 | Then edit `conf.py` to use this extension.
29 |
30 | ```python
31 | extensions = [
32 | "sphinx_new_tab_link",
33 | ]
34 | ```
35 |
36 | ## Configuration
37 |
38 | ### `new_tab_link_show_external_link_icon`
39 |
40 | * Type: `bool`
41 | * Default: `False`
42 |
43 | If you want to show external links with icons, set this to `True` in your `conf.py`.
44 |
45 | ```python
46 | new_tab_link_show_external_link_icon = True
47 | ```
48 |
49 | ### `new_tab_link_enable_referrer`
50 |
51 | * Type: `bool`
52 | * Default: `False`
53 |
54 | If you want external links without `rel="noreferrer"`, set this to `True` in your `conf.py`.
55 |
56 | ```python
57 | new_tab_link_enable_referrer = True
58 | ```
59 |
60 | ## Roles
61 |
62 | ### External link with icon (Experimental)
63 |
64 | ```rst
65 | External link: :icon-link:`Example `
66 | ```
67 |
68 | Enjoy documentation!🙌
69 |
--------------------------------------------------------------------------------
/tests/helpers.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import shutil
4 | from typing import TYPE_CHECKING
5 |
6 | from bs4 import BeautifulSoup
7 |
8 | if TYPE_CHECKING:
9 | from pathlib import Path
10 |
11 |
12 | def prepare_files(test_root: Path, test_tempdir: Path) -> None:
13 | if not test_tempdir.exists():
14 | shutil.copytree(test_root, test_tempdir)
15 |
16 |
17 | def extract_references(html_path: Path):
18 | contents = html_path.read_text()
19 | soup = BeautifulSoup(contents, "html.parser")
20 | return soup.find_all("a", {"class": "reference"})
21 |
22 |
23 | def assert_reference_is_external(reference, expected_url: str) -> None:
24 | assert reference["href"] == expected_url
25 | assert_referrer_disabled_reference(reference)
26 |
27 |
28 | def assert_reference_attributes(reference) -> None:
29 | assert "external" in reference["class"]
30 | assert reference["target"] == "_blank"
31 |
32 |
33 | def assert_referrer_disabled_reference(reference) -> None:
34 | assert_reference_attributes(reference)
35 | assert reference["rel"] == ["noreferrer"]
36 |
37 |
38 | def assert_reference_is_external_with_icon(
39 | reference, expected_url: str
40 | ) -> None:
41 | assert_reference_is_external(reference, expected_url)
42 | assert reference.svg
43 |
44 |
45 | def assert_reference_is_not_external(reference) -> None:
46 | assert "internal" in reference["class"] # not external == internal
47 | assert "target" not in reference.attrs
48 | assert "rel" not in reference.attrs
49 |
50 |
51 | def assert_referrer_enabled_reference(reference) -> None:
52 | assert_reference_attributes(reference)
53 | assert "rel" not in reference.attrs
54 |
55 |
56 | def assert_reference_is_external_with_referrer(
57 | reference, expected_url: str
58 | ) -> None:
59 | assert reference["href"] == expected_url
60 | assert_referrer_enabled_reference(reference)
61 |
--------------------------------------------------------------------------------
/src/sphinx_new_tab_link/__init__.py:
--------------------------------------------------------------------------------
1 | from docutils import nodes
2 | from sphinx.application import Sphinx
3 | from sphinx.util.typing import ExtensionMetadata
4 | from sphinxcontrib.kasane import new_translator_class_for_builder
5 |
6 | from sphinx_new_tab_link.core import add_icon_to_reference
7 | from sphinx_new_tab_link.roles import IconLinkRole
8 |
9 | __VERSION__ = "0.8.1"
10 |
11 |
12 | class NewTabLinkHTMLTranslatorMixin:
13 | """Patched translator to open an external link in a new tab of the browser.
14 |
15 | ref: https://stackoverflow.com/a/67153583
16 | """
17 |
18 | def starttag(self, node: nodes.Node, tagname: str, *args, **atts):
19 | if (
20 | tagname == "a"
21 | and "target" not in atts
22 | and (
23 | "external" in atts.get("class", "")
24 | or "external" in atts.get("classes", [])
25 | )
26 | ):
27 | assert isinstance(node, nodes.reference)
28 | atts["target"] = "_blank"
29 | if not self.builder.config.new_tab_link_enable_referrer: # type: ignore[attr-defined] # noqa: E501
30 | atts["rel"] = "noreferrer"
31 | if self.builder.config.new_tab_link_show_external_link_icon: # type: ignore[attr-defined] # noqa: E501
32 | node = add_icon_to_reference(node)
33 | return super().starttag(node, tagname, *args, **atts) # type: ignore[misc] # noqa: E501
34 |
35 |
36 | def setup(app: Sphinx) -> ExtensionMetadata:
37 | app.add_config_value("new_tab_link_show_external_link_icon", False, "html")
38 | app.add_config_value("new_tab_link_enable_referrer", False, "html")
39 | app.add_role("icon-link", IconLinkRole())
40 |
41 | html_translator_handler = new_translator_class_for_builder(
42 | "html", NewTabLinkHTMLTranslatorMixin, "NewTabLinkHTMLTranslator"
43 | )
44 | app.connect("builder-inited", html_translator_handler)
45 |
46 | return ExtensionMetadata(version=__VERSION__, parallel_read_safe=True)
47 |
--------------------------------------------------------------------------------
/tests/test_sphinx_new_tab_link.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | import pytest
6 |
7 | from .helpers import (
8 | assert_reference_is_external,
9 | assert_reference_is_not_external,
10 | extract_references,
11 | )
12 |
13 | if TYPE_CHECKING:
14 | from pathlib import Path
15 |
16 |
17 | @pytest.fixture(params=["html", "singlehtml", "dirhtml"])
18 | def builder(request) -> str:
19 | return request.param
20 |
21 |
22 | @pytest.fixture
23 | def directory_name() -> str:
24 | return "default"
25 |
26 |
27 | @pytest.mark.parametrize(
28 | "index,expected_url",
29 | [
30 | (0, "http://abehiroshi.la.coocan.jp"),
31 | (1, "https://pypi.org/project/sphinx-new-tab-link/"),
32 | (
33 | 2,
34 | "https://www.sphinx-doc.org/en/master/usage/restructuredtext/"
35 | "basics.html",
36 | ),
37 | (3, "https://example.com/"),
38 | (4, "https://httpbin.org/"),
39 | (5, "https://example.com/"),
40 | (6, "https://github.com/ftnext/sphinx-new-tab-link"),
41 | (
42 | 7,
43 | "https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/", # NOQA: E501
44 | ),
45 | ],
46 | ids=[
47 | "url_only_line",
48 | "inline_external_link",
49 | "refer_external_link_target",
50 | "embed_external_link_with_target_definition",
51 | "embed_anonymous_external_link",
52 | "refer_embedded_external_link_target_again",
53 | "refer_anonymous_external_link_target",
54 | "image_directive_target",
55 | ],
56 | )
57 | def test_should_open_new_tab(
58 | built_html_path: Path,
59 | index: int,
60 | expected_url: str,
61 | ) -> None:
62 | references = extract_references(built_html_path)
63 |
64 | assert_reference_is_external(references[index], expected_url)
65 |
66 |
67 | def test_internal_link_should_not_open_new_tab(built_html_path: Path) -> None:
68 | references = extract_references(built_html_path)
69 |
70 | assert_reference_is_not_external(references[-1])
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "sphinx-new-tab-link"
3 | description = "Open external links in new tabs of the browser in Sphinx HTML documents"
4 | readme = { file = "README.md", content-type = "text/markdown" }
5 | requires-python = ">=3.10"
6 | license = { text = "MIT License" }
7 | authors = [{ name = "nikkie", email = "takuyafjp+develop@gmail.com" }]
8 | classifiers = [
9 | "Development Status :: 3 - Alpha",
10 | "License :: OSI Approved :: MIT License",
11 | "Intended Audience :: Developers",
12 | "Framework :: Sphinx",
13 | "Framework :: Sphinx :: Extension",
14 | "Topic :: Documentation",
15 | "Topic :: Documentation :: Sphinx",
16 | "Topic :: Internet :: WWW/HTTP :: Site Management",
17 | "Topic :: Software Development :: Documentation",
18 | "Topic :: Text Processing :: Markup :: HTML",
19 | "Programming Language :: Python",
20 | "Programming Language :: Python :: 3",
21 | "Programming Language :: Python :: 3 :: Only",
22 | "Programming Language :: Python :: 3.10",
23 | "Programming Language :: Python :: 3.11",
24 | "Programming Language :: Python :: 3.12",
25 | "Programming Language :: Python :: 3.13",
26 | "Programming Language :: Python :: 3.14",
27 | ]
28 | dependencies = ["Sphinx>=7.3", "sphinxcontrib-extdevhelper-kasane"]
29 | dynamic = ["version"]
30 |
31 | [project.optional-dependencies]
32 | testing = ["pytest", "pytest-randomly", "defusedxml", "beautifulsoup4"]
33 | typecheck = ["mypy", "types-docutils", "types-beautifulsoup4"]
34 | lint = ["flake8", "black", "isort", "autoflake", "pyupgrade"]
35 | dev = ["taskipy"]
36 |
37 | [project.urls]
38 | Homepage = "https://github.com/ftnext/sphinx-new-tab-link"
39 | Guide = "https://ftnext.github.io/sphinx-new-tab-link/guide.en.html"
40 | "Bug Tracker" = "https://github.com/ftnext/sphinx-new-tab-link/issues"
41 |
42 | [tool.setuptools.dynamic]
43 | version = { attr = "sphinx_new_tab_link.__VERSION__" }
44 |
45 | [tool.mypy]
46 | exclude = ["tests/roots"]
47 | ignore_missing_imports = true
48 |
49 | [tool.taskipy.tasks]
50 | pre_test = "task format"
51 | test = "pytest -v -x --ff"
52 | post_test = "task check"
53 | format = "task format_autoflake && task format_pyupgrade && task format_black && task format_isort"
54 | format_autoflake = "autoflake --in-place --remove-all-unused-imports $(find src tests -name '*.py') setup.py"
55 | format_pyupgrade = "pyupgrade $(find src tests -name '*.py') setup.py"
56 | format_black = "black -l 79 src tests setup.py"
57 | format_isort = "isort --profile black -l 79 src tests setup.py"
58 | check = "task check_flake8 && task check_mypy"
59 | check_flake8 = "flake8 src tests"
60 | check_mypy = "mypy src tests"
61 |
--------------------------------------------------------------------------------
/docs/guide.rst:
--------------------------------------------------------------------------------
1 | .. _japanese_guide:
2 |
3 | ``sphinx-new-tab-link`` 使い方ガイド
4 | =============================================================
5 |
6 | | ``sphinx-new-tab-link`` はSphinx拡張です。
7 | | ソースからビルドしたHTMLで、 **外部リンクをブラウザの新しいタブで開く** ように自動で設定します✨
8 | | このドキュメントで使い方を示します(なんと、このドキュメントはドッグフーディングにもなっているんですよ!🐶)
9 |
10 | *Here is English version of this guide:* :ref:`english_guide`
11 |
12 | リンク集
13 | --------------------
14 |
15 | * PyPI https://pypi.org/project/sphinx-new-tab-link/
16 | * `GitHub Repository `_
17 |
18 | インストール
19 | --------------------
20 |
21 | ``sphinx-new-tab-link`` はPyPIからインストールできます。
22 |
23 | .. code-block:: shell
24 |
25 | $ pip install sphinx-new-tab-link
26 |
27 | 使い方
28 | --------------------
29 |
30 | あなたのSphinxプロジェクトの :file:`conf.py` でこの拡張を有効にしてください。
31 |
32 | .. code-block:: python
33 | :caption: conf.py
34 |
35 | extensions = [
36 | "sphinx_new_tab_link",
37 | ]
38 |
39 | これだけです!
40 |
41 | HTML中の外部リンクが、ブラウザの新しいタブで開かれるようになります。
42 |
43 | 対応している記法
44 | --------------------
45 |
46 | reSTで可能なさまざまな記法による外部リンクをサポートしています。
47 |
48 | ✅直接URLだけを書いた行
49 |
50 | https://github.com/ftnext/sphinx-new-tab-link
51 |
52 | ✅インラインで書いたURL(「リンク集」の *PyPI* 参照)
53 |
54 | .. _公開版ガイド: https://ftnext.github.io/sphinx-new-tab-link/guide.html
55 |
56 | ✅外部ハイパーリンクターゲットを定義し、それを参照: `公開版ガイド`_
57 |
58 | .. code-block:: rest
59 |
60 | .. _公開版ガイド: https://ftnext.github.io/sphinx-new-tab-link/guide.html
61 |
62 | 外部ハイパーリンクターゲットを定義し、それを参照: `公開版ガイド`_
63 |
64 | .. __: https://ftnext.github.io/sphinx-new-tab-link/guide.html
65 |
66 | ✅匿名ハイパーリンク記法: `公開版ガイド(匿名記法)`__
67 |
68 | .. code-block:: rest
69 |
70 | .. __: https://ftnext.github.io/sphinx-new-tab-link/guide.html
71 |
72 | 匿名ハイパーリンク記法: `公開版ガイド(匿名記法)`__
73 |
74 | ✅アンダースコア2つの埋め込みURL: `GitHub Repository(ターゲット定義なし) `__
75 |
76 | .. code-block:: rst
77 |
78 | `GitHub Repository(ターゲット定義なし) `__
79 |
80 | ✅アンダースコア1つの埋め込みURLによって定義したターゲット(リンク集の *GitHub Repository*)を再度参照: `GitHub Repository`_
81 |
82 | .. code-block:: rst
83 |
84 | `GitHub Repository `_
85 |
86 | 再度 `GitHub Repository`_ を参照できる
87 |
88 | ✅ ``:target:`` オプションを指定した ``image`` ディレクティブ
89 |
90 | .. image:: _static/breakfast.jpg
91 | :target: https://www.flickr.com/photos/pyconjp/48818171768/in/album-72157710870622516/
92 |
93 | .. code-block:: rst
94 |
95 | .. image:: _static/breakfast.jpg
96 | :target: https://www.flickr.com/photos/pyconjp/48818171768/in/album-72157710870622516/
97 |
98 | ✅ ``:target:`` オプションを指定した ``figure`` ディレクティブ
99 |
100 | .. figure:: _static/pyconjp2019.jpg
101 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
102 |
103 | .. code-block:: rst
104 |
105 | .. figure:: _static/pyconjp2019.jpg
106 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
107 |
108 | .. note:: `sphinx.ext.autodoc `__ にも対応しています!
109 |
110 | URLを含んだdocstringからビルドしたHTMLでも、URLはブラウザの新しいタブで開きます。
111 | ``sphinx-new-tab-link`` の :doc:`api` のドキュメントは、その例になっているんですよ!🐶
112 |
113 | Enjoy documentation!🙌
114 |
--------------------------------------------------------------------------------
/docs/guide.en.rst:
--------------------------------------------------------------------------------
1 | .. _english_guide:
2 |
3 | Guide of ``sphinx-new-tab-link``
4 | =============================================================
5 |
6 | | ``sphinx-new-tab-link`` is a Sphinx extension.
7 | | It builds HTML from source which enables **your browser to open external links in new tabs** ✨
8 | | I'll show you how to use it in this document (oh my gosh, this document is dogfooding too! 🐶)
9 |
10 | *このガイドの日本語版はこちらにあります:* :ref:`japanese_guide`
11 |
12 | Links
13 | --------------------
14 |
15 | * PyPI https://pypi.org/project/sphinx-new-tab-link/
16 | * `GitHub Repository `_
17 |
18 | Installation
19 | --------------------
20 |
21 | You can install ``sphinx-new-tab-link`` from PyPI.
22 |
23 | .. code-block:: shell
24 |
25 | $ pip install sphinx-new-tab-link
26 |
27 | How to use
28 | --------------------
29 |
30 | Enable this extension in your Sphinx project's :file:`conf.py`.
31 |
32 | .. code-block:: python
33 | :caption: conf.py
34 |
35 | extensions = [
36 | "sphinx_new_tab_link",
37 | ]
38 |
39 | That's it!
40 |
41 | External links in HTML will be opened in a new tab of the browser.
42 |
43 | Supported notations
44 | --------------------
45 |
46 | It supports various notations of a external link possible in reST.
47 |
48 | ✅Line with URL only
49 |
50 | https://github.com/ftnext/sphinx-new-tab-link
51 |
52 | ✅URL written inline (See *PyPI* at 'Links' section)
53 |
54 | .. _Published guide: https://ftnext.github.io/sphinx-new-tab-link/guide.html
55 |
56 | ✅Define an external hyperlink target and refer it: `Published guide`_
57 |
58 | .. code-block:: rest
59 |
60 | .. _Published guide: https://ftnext.github.io/sphinx-new-tab-link/guide.html
61 |
62 | Define an external hyperlink target and refer it: `Published guide`_
63 |
64 | .. __: https://ftnext.github.io/sphinx-new-tab-link/guide.html
65 |
66 | ✅Anonymous hyperlink notation: `published guide (anonymous notation)`__
67 |
68 | .. code-block:: rest
69 |
70 | .. __: https://ftnext.github.io/sphinx-new-tab-link/guide.html
71 |
72 | Anonymous hyperlink notation: `published guide (anonymous notation)`__
73 |
74 | ✅Embedded URL with double underscore: `GitHub Repository (without target definition) `__
75 |
76 | .. code-block:: rst
77 |
78 | `GitHub Repository (without target definition) `__
79 |
80 | ✅Refer again to the target defined by the embedded URL with single underscore (See *GitHub Repository* at 'Links' section): `GitHub Repository`_
81 |
82 | .. code-block:: rst
83 |
84 | `GitHub Repository `_
85 |
86 | Can refer `GitHub Repository`_ again.
87 |
88 | ✅ ``image`` directive specified ``:target:`` option
89 |
90 | .. image:: _static/breakfast.jpg
91 | :target: https://www.flickr.com/photos/pyconjp/48818171768/in/album-72157710870622516/
92 |
93 | .. code-block:: rst
94 |
95 | .. image:: _static/breakfast.jpg
96 | :target: https://www.flickr.com/photos/pyconjp/48818171768/in/album-72157710870622516/
97 |
98 | ✅ ``figure`` directive specified ``:target:`` option
99 |
100 | .. figure:: _static/pyconjp2019.jpg
101 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
102 |
103 | .. code-block:: rst
104 |
105 | .. figure:: _static/pyconjp2019.jpg
106 | :target: https://www.flickr.com/photos/pyconjp/48743997848/in/album-72157710870622516/
107 |
108 | .. note:: note: Also supports `sphinx.ext.autodoc `__!
109 |
110 | For HTML built from a docstring containing a URL, your browser open the URL in a new tab.
111 | The :doc:`api` documentation of ``sphinx-new-tab-link`` is an example of that! 🐶
112 |
113 | Enjoy documentation!🙌
114 |
--------------------------------------------------------------------------------