Skip to content

Commit e60eec7

Browse files
EpithumiaFredZin
andauthored
Merge Chemins relatifs dans main (#14)
* FIX - MAJ automatique du rendu des IDE lors des changements d'onglets mkdocs/material. * ADD - support pour les chemins relatifs au fichier markdown en cours (noms de fichiers sql) * ADD - Ajouts de la logistique pour tester dans la doc * CHANGE - Discover and add css and js files automatically * CHANGE - Remove useless `LIBS_PATH` --------- Co-authored-by: Frédéric Zinelli <frederic.zinelli@gmail.com>
1 parent e810488 commit e60eec7

File tree

8 files changed

+190
-62
lines changed

8 files changed

+190
-62
lines changed

docs/essai/bac_a_sable.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Essai
2+
3+
(cette page n'est pas rendue sur le site, par défaut. L'activer via mkdocs.yml pour tester les chemins relatifs au fichier md en cours)_
4+
5+
## Sans le plugin macro activé, ancienne syntaxe
6+
7+
A modifier en cas de test avec le plugin macro activé
8+
9+
=== "onglet 1"
10+
11+
{{ sqlide titre="IDE avec initialisation et code pré-saisi" init="init1.sql" sql="code.sql" }}
12+
13+
=== "onglet 2"
14+
15+
{{ sqlide titre="IDE avec une base binaire chargée et code pré-saisi autoexécuté" base="../bases/test.db" sql="../sql/code.sql" autoexec}}
16+
17+
## Avec le plugin macro activé, ancienne syntaxe
18+
19+
=== "onglet 1"
20+
21+
{!{ sqlide titre="IDE avec initialisation et code pré-saisi" init="init1.sql" sql="code.sql" }!}
22+
23+
=== "onglet 2"
24+
25+
{!{ sqlide titre="IDE avec une base binaire chargée et code pré-saisi autoexécuté" base="../bases/test.db" sql="../sql/code.sql" autoexec}!}
26+
27+
## Nouvelle syntaxe
28+
29+
=== "onglet 1"
30+
31+
{{ sqlide("IDE avec initialisation et code pré-saisi", init="init1.sql", sql="code.sql") }}
32+
33+
=== "onglet 2"
34+
35+
{{ sqlide("IDE avec initialisation et code pré-saisi", init="init1.sql", sql="code.sql") }}

docs/essai/code.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SELECT *
2+
FROM employees;
3+
4+
SELECT COUNT(*) FROM employees;

docs/essai/init1.sql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
DROP TABLE IF EXISTS employees;
2+
CREATE TABLE employees
3+
(
4+
id integer,
5+
name text,
6+
designation text,
7+
manager integer,
8+
hired_on date,
9+
salary integer,
10+
commission float,
11+
dept integer
12+
);
13+
14+
INSERT INTO employees VALUES (1,'JOHNSON','ADMIN',6,'1990-12-17',18000,NULL,4);
15+
INSERT INTO employees VALUES (2,'HARDING','MANAGER',9,'1998-02-02',52000,300,3);
16+
INSERT INTO employees VALUES (3,'TAFT','SALES I',2,'1996-01-02',25000,500,3);
17+
INSERT INTO employees VALUES (4,'HOOVER','SALES I',2,'1990-04-02',27000,NULL,3);
18+
INSERT INTO employees VALUES (5,'LINCOLN','TECH',6,'1994-06-23',22500,1400,4);
19+
INSERT INTO employees VALUES (6,'GARFIELD','MANAGER',9,'1993-05-01',54000,NULL,4);

mkdocs.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ nav:
1616
- 'License': 'licence.md'
1717
- 'Historique des versions': 'notes-de-version.md'
1818

19+
# - Bac à sable: 'essai/bac_a_sable.md' # testing purpose
20+
21+
exclude_docs: | # Désactiver tout le bloc pour activer dans la nav
22+
essai/**
23+
24+
1925
repo_url: https://github.com/epithumia/mkdocs-sqlite-console
2026
site_url: https://epithumia.github.io/mkdocs-sqlite-console
2127
edit_uri: edit/main/docs/
@@ -30,3 +36,8 @@ markdown_extensions:
3036
- pymdownx.details
3137
- pymdownx.superfences
3238
- attr_list
39+
- pymdownx.tabbed: # Volets glissants. === "Mon volet"
40+
alternate_style: true # compatibilité pour mobiles
41+
slugify: !!python/object/apply:pymdownx.slugs.slugify
42+
kwds:
43+
case: lower

mkdocs_sqlite_console/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .plugin import SQLiteConsole
22

3-
VERSION = "2.0.1"
3+
VERSION = "2.0.2"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**FIX rendering troubles for sqlides that are in secondary tabs. */
2+
(function(){
3+
4+
const delayedRefreshFactory = (content) =>()=>{
5+
setTimeout(()=>{
6+
const cms = Object.values(content.getElementsByClassName("CodeMirror"))
7+
for(const elt of cms){
8+
if(!elt.CodeMirror || !elt.CodeMirror.refresh){
9+
continue
10+
}
11+
elt.CodeMirror.refresh()
12+
}
13+
})
14+
}
15+
16+
const tabbed = Object.values(document.getElementsByClassName('tabbed-labels'))
17+
18+
for(const tab of tabbed){
19+
const labels = Object.values(tab.getElementsByTagName("label"))
20+
for(const label of labels){
21+
const iLabel = [...label.parentElement.children].indexOf(label)
22+
const content = tab.parentElement.getElementsByClassName("tabbed-content")[0].children[iLabel]
23+
label.addEventListener("click", delayedRefreshFactory(content), {once:true})
24+
}
25+
}
26+
})()

mkdocs_sqlite_console/plugin.py

Lines changed: 93 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import os
2+
from pathlib import Path
23
import re
3-
from typing import Optional
4+
from typing import Optional, Tuple
45

56
from mkdocs.exceptions import PluginError
67
from mkdocs.plugins import BasePlugin
7-
from mkdocs.structure.files import File
8+
from mkdocs.structure.files import File, Files
89
from mkdocs.structure.pages import Page
10+
from mkdocs.config.defaults import MkDocsConfig
911

1012

1113
BASE_PATH = os.path.dirname(os.path.realpath(__file__))
1214

13-
LIBS_PATH = BASE_PATH + "/libs/"
14-
1515
CSS_PATH = BASE_PATH + "/css/"
1616

1717
JS_PATH = BASE_PATH + "/js/"
1818

19+
ARG_PATTERN_TEMPLATE = "{}\\s*=\\s*[\"']?([^\\s\"']*)"
20+
1921

2022
# "{workerinit}" was the first line of the template: removed in version > 2.0.0.
2123
SKELETON = """
@@ -39,35 +41,45 @@
3941

4042

4143

44+
45+
46+
47+
4248
class Counter:
49+
4350
def __init__(self, config):
51+
self.ROOT_URI = Path(config['docs_dir']).as_posix()
4452
self.count = 0
45-
self.config = config
53+
self.config: MkDocsConfig = config
4654
self.spaces = {}
4755
self.macros_contents = {}
4856
self.worker_inits = []
4957

50-
def insert_ide(self, macro):
51-
regex_titre = r".*?titre=\"(.*?)\""
52-
regex_init = r".*?init=[\"\']?\b([^\s]*)\b"
53-
regex_base = r".*?base=[\"\']?\b([^\s]*)\b"
54-
regex_sql = r".*?sql=[\"\']?\b([^\s]*)\b"
55-
regex_space = r".*?espace=[\"\']?\b([^\s]*)\b"
58+
def insert_ide(self, page:Page):
59+
def wrapped(matches):
5660

57-
params = str(macro.groups(0)[0])
61+
regex_titre = r".*?titre\s*=\s*\"(.*?)\""
62+
regex_init = ARG_PATTERN_TEMPLATE.format('init')
63+
regex_base = ARG_PATTERN_TEMPLATE.format('base')
64+
regex_sql = ARG_PATTERN_TEMPLATE.format('sql')
65+
regex_space = ARG_PATTERN_TEMPLATE.format('espace')
5866

59-
titre = "".join(re.findall(regex_titre, params))
60-
autoexec = "autoexec" in params
61-
hide = "hide" in params
62-
init = "".join(re.findall(regex_init, params))
63-
base = "".join(re.findall(regex_base, params))
64-
sql = "".join(re.findall(regex_sql, params))
65-
space = "".join(re.findall(regex_space, params))
67+
params = str(matches.groups(0)[0])
6668

67-
return self.build_sql(titre, autoexec, hide, init, base, sql, space)
69+
titre = "".join(re.findall(regex_titre, params))
70+
autoexec = "autoexec" in params
71+
hide = "hide" in params
72+
init = "".join(re.findall(regex_init, params))
73+
base = "".join(re.findall(regex_base, params))
74+
sql = "".join(re.findall(regex_sql, params))
75+
space = "".join(re.findall(regex_space, params))
6876

69-
def build_sql(self, titre, autoexec, hide, init, base, sql, space):
77+
return self.build_sql(titre, autoexec, hide, init, base, sql, space, page=page)
78+
return wrapped
79+
80+
def build_sql(self, titre, autoexec, hide, init, base, sql, space, *, page:Page=None):
7081
self.count += 1
82+
worker = ""
7183

7284
# handle defaults (centralizing the logic in one single place):
7385
autoexec = True if autoexec else ""
@@ -78,7 +90,6 @@ def build_sql(self, titre, autoexec, hide, init, base, sql, space):
7890
sql = sql or ""
7991
space = space or None
8092

81-
worker = ""
8293
if space:
8394
if space not in self.spaces:
8495
self.spaces[space] = 0
@@ -90,37 +101,28 @@ def build_sql(self, titre, autoexec, hide, init, base, sql, space):
90101
else:
91102
self.spaces[space] += 1
92103
worker = space
104+
93105
if sql != "":
94-
try:
95-
with open(os.path.abspath(self.config["docs_dir"]) + "/" + sql) as f:
96-
sql = f.readlines()
97-
sql = "".join(sql)
98-
if autoexec:
99-
autoexec = sql
100-
autoexec = autoexec.replace("\n", "\\n")
101-
autoexec = autoexec.replace("'", "\\'")
102-
except OSError:
103-
sql = "Fichier '" + sql + "' introuvable"
106+
ok, sql, _ = self.get_relative_file(page, sql, "Fichier")
107+
if ok and autoexec:
108+
autoexec = sql.replace("\n", "\\n").replace("'", "\\'")
109+
104110
if init != "":
105-
try:
106-
with open(os.path.abspath(self.config["docs_dir"]) + "/" + init) as f:
107-
init = f.readlines()
108-
init = "".join(init)
109-
init = init.replace("\n", "\\n")
110-
init = init.replace("'", "\\'")
111-
except OSError:
112-
sql = "-- Fichier d'initialisation '" + init + "' introuvable"
113-
init = ""
111+
ok, init, _ = self.get_relative_file(page, init, "-- Fichier d'initialisation")
112+
init = init.replace("\n", "\\n").replace("'", "\\'")
113+
if not ok:
114+
sql, init = init, ""
115+
114116
if base != "/":
115-
base_url = self.config["site_url"]
116-
if os.path.isfile(os.path.abspath(self.config["docs_dir"]) + "/" + base):
117-
base = base_url + "/" + base
118-
base = base.replace("//", "/")
117+
ok, nope, resolved_path = self.get_relative_file(page, base, "-- Fichier de base")
118+
if ok:
119+
base = Path(resolved_path).as_posix().replace(self.ROOT_URI, self.config['site_url'])
119120
else:
120-
sql = "-- Fichier de base '" + base + "' introuvable"
121+
sql = nope
121122
init = ""
122123
base = "/"
123-
html = SKELETON.format(
124+
125+
ide_html = SKELETON.format(
124126
numide=self.count,
125127
title=titre,
126128
hide=hide,
@@ -131,7 +133,35 @@ def build_sql(self, titre, autoexec, hide, init, base, sql, space):
131133
worker=worker,
132134
#workerinit=workerinit, # Not inserted here anymore (version > 2.0.0)
133135
)
134-
return html
136+
return ide_html
137+
138+
def get_relative_file(self, page:Page, rel_path:str, header:str) -> Tuple[bool, str, str]:
139+
"""
140+
Extract a file relative to the current markdown file or to the docs dir, or
141+
return a default message.
142+
"""
143+
docs = self.config["docs_dir"]
144+
145+
candidates_uris_srcs = [
146+
f"{ docs }/{ Path(page.file.src_uri).parent.as_posix() }", # Relative to current page
147+
docs, # Relative to docs_dir
148+
]
149+
for cnd in candidates_uris_srcs:
150+
path_uri = f"{ cnd }/{ rel_path }"
151+
path = os.path.normpath(os.path.abspath(path_uri))
152+
153+
if not os.path.isfile(path):
154+
continue
155+
156+
if path.endswith('.db'):
157+
# .db are not readable with utf-8, but no need of their content so no problem.
158+
content = '_dummy'
159+
else:
160+
with open(path, 'r', encoding='utf-8') as f:
161+
content = f.read()
162+
return True, content, path
163+
164+
return False, f"{ header } { rel_path } introuvable.", rel_path
135165

136166
def register_macro_content(self, html_code):
137167
"""
@@ -188,15 +218,12 @@ def on_config(self, config, **kwargs):
188218

189219
return config
190220

191-
def on_files(self, files, config):
192-
files.append(
193-
File("sqlite_ide.css", CSS_PATH, config["site_dir"] + "/css/", False)
194-
)
195-
files.append(File("sqlite_ide.js", JS_PATH, config["site_dir"] + "/js/", False))
196-
files.append(
197-
File("worker.sql-wasm.js", JS_PATH, config["site_dir"] + "/js/", False)
198-
)
199-
files.append(File("sql-wasm.wasm", JS_PATH, config["site_dir"] + "/js/", False))
221+
def on_files(self, files:Files, config):
222+
for folder in "css js".split():
223+
for file in (Path(BASE_PATH) / folder).iterdir():
224+
files.append(
225+
File(file.name, file.parent, config["site_dir"] + f"/{ folder }/", False)
226+
)
200227
return files
201228

202229
def on_pre_page(self, page, config, files):
@@ -220,7 +247,7 @@ def on_page_content(self, html, page, config, files):
220247
regex = r"(?:^|\n)\s*?<p>\s*?{{\s*?sqlide.*?\s+?(.*?)\s*?}}</p>(?:\n|$)"
221248

222249
if c:
223-
html = re.sub(regex, c.insert_ide, html, flags=re.MULTILINE | re.DOTALL)
250+
html = re.sub(regex, c.insert_ide(page), html, flags=re.MULTILINE | re.DOTALL)
224251

225252
return html
226253

@@ -262,7 +289,11 @@ def on_page_context(self, ctx, page:Page, config, **kwargs):
262289

263290
# Mutate the page content with all the required logistic if any:
264291
if sql_scripts:
265-
page.content = sql_scripts + page.content
292+
page.content = f"""\
293+
{ sql_scripts }
294+
{ page.content }
295+
<script src="{ base_url }/js/material-tabbed-fix.js"></script>
296+
"""
266297

267298
def counter_for(self, page, *, set_counter: Counter = None) -> Optional[Counter]:
268299
key = page and page.url
@@ -287,6 +318,7 @@ def sqlide(
287318
when using Pyodide-MkDocs-Theme).
288319
"""
289320
# (actual default values are handled in Counter.build_sql)
290-
c = self.counter_for(self.macros.page)
291-
html_code = c.build_sql(titre, autoexec, hide, init, base, sql, espace)
321+
page = self.macros.page
322+
c = self.counter_for(page)
323+
html_code = c.build_sql(titre, autoexec, hide, init, base, sql, espace, page=page)
292324
return c.register_macro_content(html_code)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta"
66
name = "mkdocs-sqlite-console"
77
authors = [
88
{ name = "Rafael Lopez", email = "rafael.lopez@universite-paris-saclay.fr" },
9+
{ name = "Frédéric Zinelli", email = "frederic.zinelli@gmail.com"}
910
]
1011
description = "Mkdocs-SQLite-Console est un plugin pour MkDocs, qui permet d'afficher un IDE SQL (SQLite) permettant d'exécuter du code."
1112
readme = "README.md"

0 commit comments

Comments
 (0)