From 8d3b2f10158e2f54f54b864b5fe104fad5c25194 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Tue, 7 Mar 2023 12:14:31 -0800
Subject: [PATCH 1/7] Start a script to generate a tags file.

---
 .gitignore    |  1 +
 bin/make_tags | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100755 bin/make_tags

diff --git a/.gitignore b/.gitignore
index 31ec63724..1bff54ad1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@
 /.build
 /Package.resolved
 /TSPL.docc/header.html
+/TSPL.docc/tags
 /swift-book
diff --git a/bin/make_tags b/bin/make_tags
new file mode 100755
index 000000000..b53e07299
--- /dev/null
+++ b/bin/make_tags
@@ -0,0 +1,53 @@
+#! /usr/bin/env python3
+
+"""
+Generates a tags file for navigation in editors by DocC links and within
+the formal grammar.
+
+Output uses the POSIX ctags syntax, but needs a post-processing step to
+sort it.
+
+Run this script as:
+
+    bin/make_tags | sort > TSPL.docc/tags
+
+To use these tags, configure your editor to include a-z, A-Z, pound (#),
+hyphen (-), and colon (:), as valid characters in a tag.
+"""
+
+import os
+import re
+
+CHAPTER_REGEX = re.compile(r'^# ')
+HEADING_REGEX = re.compile(r'^#+ ')
+GRAMMAR_REGEX = re.compile(r'^> \*[a-z-]*\* → ')
+
+chapter = None
+
+os.chdir('TSPL.docc')
+for root, dirs, files in os.walk('.'):
+    for file in files:
+        if not file.endswith('.md'):
+            continue
+        if file == 'SummaryOfTheGrammar.md':
+            continue
+        path = os.path.join(root, file)
+        with open(path, encoding='utf8') as f:
+            chapter = None
+            for line in f:
+                line = line.rstrip()
+                if CHAPTER_REGEX.match(line):
+                    chapter = line.lstrip('# ').replace(' ', '')
+                    tag = 'doc:' + chapter
+                    search =  '/^' + line + '/'
+                    print(tag + '\t' + path + '\t' + search)
+                elif HEADING_REGEX.match(line):
+                    heading = line.lstrip('# ').replace(' ', '-')
+                    tag = 'doc:' + chapter + '#' + heading
+                    search =  '/^' + line + '/'
+                    print(tag + '\t' + path + '\t' + search)
+                elif GRAMMAR_REGEX.match(line):
+                    tag, _ = line.split('→')
+                    tag = tag.lstrip('*> ').rstrip('* ')
+                    search = '/*' + tag + '* →/'
+                    print(tag + '\t' + path + '\t' + search)

From d3b27ad494fc403922700af50866a9c2ac610694 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Mon, 20 Mar 2023 17:12:37 -0700
Subject: [PATCH 2/7] Write sorted output to the tags file.

---
 bin/make_tags | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/bin/make_tags b/bin/make_tags
index b53e07299..d96390c3a 100755
--- a/bin/make_tags
+++ b/bin/make_tags
@@ -4,12 +4,7 @@
 Generates a tags file for navigation in editors by DocC links and within
 the formal grammar.
 
-Output uses the POSIX ctags syntax, but needs a post-processing step to
-sort it.
-
-Run this script as:
-
-    bin/make_tags | sort > TSPL.docc/tags
+Output uses the POSIX ctags syntax.
 
 To use these tags, configure your editor to include a-z, A-Z, pound (#),
 hyphen (-), and colon (:), as valid characters in a tag.
@@ -23,6 +18,7 @@ HEADING_REGEX = re.compile(r'^#+ ')
 GRAMMAR_REGEX = re.compile(r'^> \*[a-z-]*\* → ')
 
 chapter = None
+tags = []
 
 os.chdir('TSPL.docc')
 for root, dirs, files in os.walk('.'):
@@ -40,14 +36,18 @@ for root, dirs, files in os.walk('.'):
                     chapter = line.lstrip('# ').replace(' ', '')
                     tag = 'doc:' + chapter
                     search =  '/^' + line + '/'
-                    print(tag + '\t' + path + '\t' + search)
+                    tags.append(tag + '\t' + path + '\t' + search + '\n')
                 elif HEADING_REGEX.match(line):
                     heading = line.lstrip('# ').replace(' ', '-')
                     tag = 'doc:' + chapter + '#' + heading
                     search =  '/^' + line + '/'
-                    print(tag + '\t' + path + '\t' + search)
+                    tags.append(tag + '\t' + path + '\t' + search + '\n')
                 elif GRAMMAR_REGEX.match(line):
                     tag, _ = line.split('→')
                     tag = tag.lstrip('*> ').rstrip('* ')
                     search = '/*' + tag + '* →/'
-                    print(tag + '\t' + path + '\t' + search)
+                    tags.append(tag + '\t' + path + '\t' + search + '\n')
+
+tags.sort()
+with open('tags', 'w', encoding='utf8') as tags_file:
+    tags_file.writelines(tags)

From 4fc78245128340bf132ca7a8257d4556a3556820 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Mon, 20 Mar 2023 17:18:53 -0700
Subject: [PATCH 3/7] Escape slashes in the search pattern.

There aren't any headings or grammatical category names today that
include a slash, and there aren't likely to ever be any.  This change
prevents generating incorrect search patterns in the tags file in the
future, just in case, and it's better to be correct anyhow.
---
 bin/make_tags | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/bin/make_tags b/bin/make_tags
index d96390c3a..80b5407bf 100755
--- a/bin/make_tags
+++ b/bin/make_tags
@@ -35,17 +35,17 @@ for root, dirs, files in os.walk('.'):
                 if CHAPTER_REGEX.match(line):
                     chapter = line.lstrip('# ').replace(' ', '')
                     tag = 'doc:' + chapter
-                    search =  '/^' + line + '/'
+                    search =  '/^' + line.replace('/', '\\/') + '/'
                     tags.append(tag + '\t' + path + '\t' + search + '\n')
                 elif HEADING_REGEX.match(line):
                     heading = line.lstrip('# ').replace(' ', '-')
                     tag = 'doc:' + chapter + '#' + heading
-                    search =  '/^' + line + '/'
+                    search =  '/^' + line.replace('/', '\\/') + '/'
                     tags.append(tag + '\t' + path + '\t' + search + '\n')
                 elif GRAMMAR_REGEX.match(line):
                     tag, _ = line.split('→')
                     tag = tag.lstrip('*> ').rstrip('* ')
-                    search = '/*' + tag + '* →/'
+                    search = '/*' + tag.replace('/', '\\/') + '* →/'
                     tags.append(tag + '\t' + path + '\t' + search + '\n')
 
 tags.sort()

From 7f4372e95732e0f8cef43e180189a3b14cfdbbd1 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Tue, 21 Mar 2023 12:00:06 -0700
Subject: [PATCH 4/7] Add the Swift source file comment.

---
 bin/make_tags | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/bin/make_tags b/bin/make_tags
index 80b5407bf..650d7b1d7 100755
--- a/bin/make_tags
+++ b/bin/make_tags
@@ -1,5 +1,13 @@
 #! /usr/bin/env python3
 
+# This source file is part of the Swift.org open source project
+# 
+# Copyright (c) 2023 Apple Inc. and the Swift project authors
+# Licensed under Apache License v2.0 with Runtime Library Exception
+# 
+# See https://swift.org/LICENSE.txt for license information
+# See https://swift.org/CONTRIBUTORS.txt for Swift project authors
+
 """
 Generates a tags file for navigation in editors by DocC links and within
 the formal grammar.

From d4c400e9cebaa8cc4f554a3a6a4854d9266c0a77 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Fri, 24 Mar 2023 15:48:21 -0700
Subject: [PATCH 5/7] Revise the script's summary.

---
 bin/make_tags | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/bin/make_tags b/bin/make_tags
index 650d7b1d7..4d826393e 100755
--- a/bin/make_tags
+++ b/bin/make_tags
@@ -9,10 +9,9 @@
 # See https://swift.org/CONTRIBUTORS.txt for Swift project authors
 
 """
-Generates a tags file for navigation in editors by DocC links and within
-the formal grammar.
-
-Output uses the POSIX ctags syntax.
+Generates a tags file, using the POSIX ctags syntax, for navigation while
+editing.  The tags file provides "jump to definition" information about DocC
+links to headings and syntactic categories in the formal grammar.
 
 To use these tags, configure your editor to include a-z, A-Z, pound (#),
 hyphen (-), and colon (:), as valid characters in a tag.

From 323465eda07ad88b82cb767a546a3e85587de359 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Mon, 3 Apr 2023 21:33:57 -0700
Subject: [PATCH 6/7] Sketch a Swift version of the script.

---
 bin/make_tags.swift | 53 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 bin/make_tags.swift

diff --git a/bin/make_tags.swift b/bin/make_tags.swift
new file mode 100644
index 000000000..0cace6d10
--- /dev/null
+++ b/bin/make_tags.swift
@@ -0,0 +1,53 @@
+#! /usr/bin/env python3
+
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2023 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
+
+/*
+Generates a tags file, using the POSIX ctags syntax, for navigation while
+editing.  The tags file provides "jump to definition" information about DocC
+links to headings and syntactic categories in the formal grammar.
+
+To use these tags, configure your editor to include a-z, A-Z, pound (#),
+hyphen (-), and colon (:), as valid characters in a tag.
+*/
+
+import Foundation
+
+let chapterRegex = try Regex("^# (.*)")
+let headingRegex = try Regex("^#+ ")
+let grammarRegex = try Regex(#"^> \*[a-z-]*\* → "#)
+
+var chapter: String? = nil
+var tags: [String] = []
+
+let basePath = "TSPL.docc/"
+let fileManager = FileManager()
+let fileEnumerator = fileManager.enumerator(atPath: basePath)!
+for case let path as String in fileEnumerator {
+    guard path.hasSuffix(".md") else { continue }
+    guard path != "ReferenceManual/SummaryOfTheGrammar.md" else { continue }
+    print(path)
+
+    let fileContents = try String(contentsOfFile: basePath + path, encoding: .utf8)
+    chapter = nil
+    for line in fileContents.split(separator: "\n") {
+        if let match = line.firstMatch(of: chapterRegex) {
+            let chapter = match.0
+            let tag = "doc:" + chapter
+            let search =  "/^" + line.replacingOccurances(of: "/", with: "\\/") + "/"
+            tags.append(tag + "\t" + path + "\t" + search + "\n")
+        } else if let match = line.firstMatch(of: headingRegex) {
+
+        } else if let match = line.firstMatch(of: grammarRegex) {
+
+        }
+    }
+}
+
+print(tags)

From 69b0ff308593179bfacd644b53742369d3397dc9 Mon Sep 17 00:00:00 2001
From: Alex Martini <amartini@apple.com>
Date: Mon, 12 Aug 2024 11:03:42 -0700
Subject: [PATCH 7/7] Remove trailing whitespace

---
 bin/make_tags | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/bin/make_tags b/bin/make_tags
index 4d826393e..51230157b 100755
--- a/bin/make_tags
+++ b/bin/make_tags
@@ -1,10 +1,10 @@
 #! /usr/bin/env python3
 
 # This source file is part of the Swift.org open source project
-# 
+#
 # Copyright (c) 2023 Apple Inc. and the Swift project authors
 # Licensed under Apache License v2.0 with Runtime Library Exception
-# 
+#
 # See https://swift.org/LICENSE.txt for license information
 # See https://swift.org/CONTRIBUTORS.txt for Swift project authors