├── 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 | ![testing workflow](https://github.com/ftnext/sphinx-new-tab-link/actions/workflows/testing.yml/badge.svg) 3 | [![PyPI version](https://badge.fury.io/py/sphinx-new-tab-link.svg)](https://badge.fury.io/py/sphinx-new-tab-link) 4 | [![Python Versions](https://img.shields.io/pypi/pyversions/sphinx-new-tab-link.svg)](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 | --------------------------------------------------------------------------------