mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 18:02:05 +00:00
follow up https://github.com/nim-lang/Nim/pull/22851 follow up https://github.com/nim-lang/Nim/pull/22873
142 lines
4.9 KiB
Nim
142 lines
4.9 KiB
Nim
#
|
|
# Nim's Runtime Library
|
|
# (c) Copyright 2022 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
|
|
## Nim `idx`:idx: file format related definitions.
|
|
|
|
import std/[strutils, syncio, hashes]
|
|
from std/os import splitFile
|
|
|
|
type
|
|
IndexEntryKind* = enum ## discriminator tag
|
|
ieMarkupTitle = "markupTitle"
|
|
## RST/Markdown title, text in `keyword` +
|
|
## HTML text in `linkTitle`
|
|
ieNimTitle = "nimTitle"
|
|
## Nim title
|
|
ieHeading = "heading" ## RST/Markdown markup heading, escaped
|
|
ieIdxRole = "idx" ## RST :idx: definition, escaped
|
|
ieNim = "nim" ## Nim symbol, unescaped
|
|
ieNimGroup = "nimgrp" ## Nim overload group, unescaped
|
|
IndexEntry* = object
|
|
kind*: IndexEntryKind ## 0.
|
|
keyword*: string ## 1.
|
|
link*: string ## 2.
|
|
linkTitle*: string ## 3. contains a prettier text for the href
|
|
linkDesc*: string ## 4. the title attribute of the final href
|
|
line*: int ## 5.
|
|
module*: string ## origin file, NOT a field in ``.idx`` file
|
|
aux*: string ## auxuliary field, NOT a field in ``.idx`` file
|
|
|
|
proc isDocumentationTitle*(hyperlink: string): bool =
|
|
## Returns true if the hyperlink is actually a documentation title.
|
|
##
|
|
## Documentation titles lack the hash. See `mergeIndexes()
|
|
## <#mergeIndexes,string>`_ for a more detailed explanation.
|
|
result = hyperlink.find('#') < 0
|
|
|
|
proc `$`*(e: IndexEntry): string =
|
|
"""("$1", "$2", "$3", "$4", $5)""" % [
|
|
e.keyword, e.link, e.linkTitle, e.linkDesc, $e.line]
|
|
|
|
proc quoteIndexColumn(text: string): string =
|
|
## Returns a safe version of `text` for serialization to the ``.idx`` file.
|
|
##
|
|
## The returned version can be put without worries in a line based tab
|
|
## separated column text file. The following character sequence replacements
|
|
## will be performed for that goal:
|
|
##
|
|
## * ``"\\"`` => ``"\\\\"``
|
|
## * ``"\n"`` => ``"\\n"``
|
|
## * ``"\t"`` => ``"\\t"``
|
|
result = newStringOfCap(text.len + 3)
|
|
for c in text:
|
|
case c
|
|
of '\\': result.add "\\"
|
|
of '\L': result.add "\\n"
|
|
of '\C': discard
|
|
of '\t': result.add "\\t"
|
|
else: result.add c
|
|
|
|
proc unquoteIndexColumn*(text: string): string =
|
|
## Returns the unquoted version generated by ``quoteIndexColumn``.
|
|
result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
|
|
|
|
proc formatIndexEntry*(kind: IndexEntryKind; htmlFile, id, term, linkTitle,
|
|
linkDesc: string, line: int):
|
|
tuple[entry: string, isTitle: bool] =
|
|
result.entry = $kind
|
|
result.entry.add('\t')
|
|
result.entry.add term
|
|
result.entry.add('\t')
|
|
result.entry.add(htmlFile)
|
|
if id.len > 0:
|
|
result.entry.add('#')
|
|
result.entry.add(id)
|
|
result.isTitle = false
|
|
else:
|
|
result.isTitle = true
|
|
result.entry.add('\t' & linkTitle.quoteIndexColumn)
|
|
result.entry.add('\t' & linkDesc.quoteIndexColumn)
|
|
result.entry.add('\t' & $line)
|
|
result.entry.add("\n")
|
|
|
|
proc parseIndexEntryKind(s: string): IndexEntryKind =
|
|
result = case s:
|
|
of "nim": ieNim
|
|
of "nimgrp": ieNimGroup
|
|
of "heading": ieHeading
|
|
of "idx": ieIdxRole
|
|
of "nimTitle": ieNimTitle
|
|
of "markupTitle": ieMarkupTitle
|
|
else: raise newException(ValueError, "unknown index entry value $1" % [s])
|
|
|
|
proc parseIdxFile*(path: string):
|
|
tuple[fileEntries: seq[IndexEntry], title: IndexEntry] =
|
|
var
|
|
f = 0
|
|
newSeq(result.fileEntries, 500)
|
|
setLen(result.fileEntries, 0)
|
|
let (_, base, _) = path.splitFile
|
|
for line in lines(path):
|
|
let s = line.find('\t')
|
|
if s < 0: continue
|
|
setLen(result.fileEntries, f+1)
|
|
let cols = line.split('\t')
|
|
result.fileEntries[f].kind = parseIndexEntryKind(cols[0])
|
|
result.fileEntries[f].keyword = cols[1]
|
|
result.fileEntries[f].link = cols[2]
|
|
if result.fileEntries[f].kind == ieIdxRole:
|
|
result.fileEntries[f].module = base
|
|
else:
|
|
if result.title.keyword.len == 0:
|
|
result.fileEntries[f].module = base
|
|
else:
|
|
result.fileEntries[f].module = result.title.keyword
|
|
|
|
result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn
|
|
result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn
|
|
result.fileEntries[f].line = parseInt(cols[5])
|
|
|
|
if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}:
|
|
result.title = result.fileEntries[f]
|
|
inc f
|
|
|
|
proc cmp*(a, b: IndexEntry): int =
|
|
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
|
|
result = cmpIgnoreStyle(a.keyword, b.keyword)
|
|
if result == 0:
|
|
result = cmpIgnoreStyle(a.link, b.link)
|
|
|
|
proc hash*(x: IndexEntry): Hash =
|
|
## Returns the hash for the combined fields of the type.
|
|
##
|
|
## The hash is computed as the chained hash of the individual string hashes.
|
|
result = x.keyword.hash !& x.link.hash
|
|
result = result !& x.linkTitle.hash
|
|
result = result !& x.linkDesc.hash
|
|
result = !$result
|