From e60901ba9e1687b6a31458baf56d13a79fa8383d Mon Sep 17 00:00:00 2001 From: stakiran Date: Wed, 19 Jun 2019 07:03:19 +0900 Subject: [PATCH] add new golang based version set. --- .gitignore | 1 + build.bat | 2 + intoc.go | 452 ++++++++++++++++++++++++++++++++++++++++++++++++++ intoc_test.go | 438 ++++++++++++++++++++++++++++++++++++++++++++++++ run.bat | 2 + test.bat | 2 + 6 files changed, 897 insertions(+) create mode 100644 .gitignore create mode 100644 build.bat create mode 100644 intoc.go create mode 100644 intoc_test.go create mode 100644 run.bat create mode 100644 test.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b883f1f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.exe diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3924a24 --- /dev/null +++ b/build.bat @@ -0,0 +1,2 @@ +@echo off +go build intoc.go diff --git a/intoc.go b/intoc.go new file mode 100644 index 0000000..01d9453 --- /dev/null +++ b/intoc.go @@ -0,0 +1,452 @@ +/* + intoc is a TOC Generator for GitHub Flarvored Markdown. +*/ +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" +) + +const ProductName = "intoc" +const ProductVersion = "0.0.1" + +func ____util___() { +} + +func success() { + os.Exit(0) +} + +func abort(msg string) { + fmt.Printf("[Error!] %s\n", msg) + os.Exit(1) +} + +func file2list(filepath string) []string { + fp, err := os.Open(filepath) + if err != nil { + abort(err.Error()) + } + defer fp.Close() + + lines := []string{} + + scanner := bufio.NewScanner(fp) + for scanner.Scan() { + line := scanner.Text() + lines = append(lines, line) + } + + return lines +} + +func list2file(filepath string, lines []string) { + fp, err := os.Create(filepath) + if err != nil { + abort(err.Error()) + } + defer fp.Close() + + writer := bufio.NewWriter(fp) + for _, line := range lines { + writer.WriteString(line + "\n") + } + writer.Flush() +} + +func isExist(filepath string) bool { + _, err := os.Stat(filepath) + return err == nil +} + +func ____funcs____() { +} + +func determinListPrefix (args Args) string{ + listPrefix := "-" + if *args.useAsterisk { + listPrefix = "*" + } + return listPrefix +} + +type Duplicator struct { + dict map[string]int +} + +func NewDuplicator() Duplicator { + dup := Duplicator{} + dup.dict = map[string]int{} // map needs explicit initialization + return dup +} + +func (dup *Duplicator) Add(key string) int { + _, isExist := dup.dict[key] + if !isExist { + dup.dict[key] = 1 + count_before_adding := 0 + return count_before_adding + } + count_before_adding := dup.dict[key] + dup.dict[key] += 1 + return count_before_adding +} + +func sectionname2anchor(sectionname string, dup *Duplicator) string { + ret := sectionname + + ret = strings.ToLower(ret) + ret = strings.Replace(ret, " ", "-", -1) + + reAsciiMarksWithoutHypenAndUnderscore := regexp.MustCompile("[!\"#$%&'\\(\\)\\*\\+,\\./:;<=>?@\\[\\\\\\]\\^`\\{\\|\\}~]") + ret = reAsciiMarksWithoutHypenAndUnderscore.ReplaceAllString(ret, "") + + reJapaneseMarks := regexp.MustCompile("[、。,.・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇‐/\~∥|…‥‘’“”()〔〕[]{}〈〉《》「」『』【】+-±×÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&*@§☆★○●◎◇◆□■△▲▽▼※〒→←↑↓〓∈∋⊆⊇⊂⊃∪∩∧∨¬⇒⇔∀∃∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬ʼn♯♭♪ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя─│┌┐┘└├┬┤┴┼━┃┏┓┛┗┣┳┫┻╋┠┯┨┷┿┝┰┥┸╂。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡ ㍻〝〟№㏍℡㊤㊥㊦㊧㊨㈱㈲㈹㍾㍽㍼≒≡∫∮∑√⊥∠∟⊿∵∩∪]") + ret = reJapaneseMarks.ReplaceAllString(ret, "") + + dupCount := dup.Add(ret) + if dupCount > 0 { + ret = fmt.Sprintf("%s-%d", ret, dupCount) + } + + return ret +} + +func line2sectioninfo(line string) (int, string) { + sectionlevel := 0 + body := "" + runeLine := []rune(line) + + for { + cnt := sectionlevel + 1 + comparer := strings.Repeat("#", cnt) + runeComparer := []rune(comparer) + + if string(runeLine[:cnt]) == string(runeComparer) { + sectionlevel += 1 + body = string(runeLine[sectionlevel:]) + continue + } + break + } + + return sectionlevel, body +} + +type SectionLine struct { + level int + body string + duplicator *Duplicator + listPrefix string + indentDepth int +} + +func NewSectionLine(level int, body string, duplicator *Duplicator, listPrefix string) SectionLine { + sl := SectionLine{} + sl.level = level + sl.body = body + sl.duplicator = duplicator + sl.listPrefix = listPrefix + return sl +} + +func (sl *SectionLine) SetIndentDepth(indentDepth int) { + sl.indentDepth = indentDepth +} + +func (sl *SectionLine) GetPlainLine() string { + ret := strings.TrimSpace(sl.body) + return ret +} + +func (sl *SectionLine) GetTocLine() string { + trimmedBody := strings.TrimSpace(sl.body) + + indentSpaces := strings.Repeat(" ", (sl.level-1)*sl.indentDepth) + mark := sl.listPrefix + text := trimmedBody + anchor := sectionname2anchor(text, sl.duplicator) + + ret := fmt.Sprintf("%s%s [%s](#%s)", indentSpaces, mark, text, anchor) + return ret +} + +func (sl *SectionLine) GetTocLineWithoutLinkformat() string { + trimmedBody := strings.TrimSpace(sl.body) + + indentSpaces := strings.Repeat(" ", (sl.level-1)*sl.indentDepth) + mark := sl.listPrefix + text := trimmedBody + + ret := fmt.Sprintf("%s%s %s", indentSpaces, mark, text) + return ret +} + +// isEditTargetLine is for judging the line is whether edit target or not. +// +// about editTarget +// - like this: "" + assert.True(t, isEditTargetLine(testeeLine, editTarget)) + + testeeLine = "" + assert.True(t, isEditTargetLine(testeeLine, editTarget)) + + testeeLine = "" + assert.False(t, isEditTargetLine(testeeLine, editTarget)) + + testeeLine = " " + assert.True(t, isEditTargetLine(testeeLine, editTarget)) +} + +func NewSectionLineTestee(level int, body string, dup *Duplicator, listmark string, indentDepth int) SectionLine { + sl := NewSectionLine(level, body, dup, listmark) + if indentDepth != 0 { + sl.SetIndentDepth(indentDepth) + } + return sl +} + +func TestSectionLine(t *testing.T) { + dup := NewDuplicator() + NoIndentDepth := 0 + + sl := NewSectionLineTestee(1, "大見出し", &dup, "-", 2) + assert.Equal(t, "大見出し", sl.GetPlainLine()) + assert.Equal(t, "- [大見出し](#大見出し)", sl.GetTocLine()) + assert.Equal(t, "- 大見出し", sl.GetTocLineWithoutLinkformat()) + + sl = NewSectionLineTestee(2, "中見出し", &dup, "-", 2) + assert.Equal(t, "中見出し", sl.GetPlainLine()) + assert.Equal(t, " - [中見出し](#中見出し)", sl.GetTocLine()) + assert.Equal(t, " - 中見出し", sl.GetTocLineWithoutLinkformat()) + + sl = NewSectionLineTestee(2, "中見出し", &dup, "-", 2) + assert.Equal(t, "中見出し", sl.GetPlainLine()) + assert.Equal(t, " - [中見出し](#中見出し-1)", sl.GetTocLine(), "Test duplicated body") + assert.Equal(t, " - 中見出し", sl.GetTocLineWithoutLinkformat()) + + sl = NewSectionLineTestee(1, "中見出しインデント無し", &dup, "-", NoIndentDepth) + assert.Equal(t, "中見出しインデント無し", sl.GetPlainLine()) + assert.Equal(t, "- [中見出しインデント無し](#中見出しインデント無し)", sl.GetTocLine()) + assert.Equal(t, "- 中見出しインデント無し", sl.GetTocLineWithoutLinkformat()) + + sl = NewSectionLineTestee(3, "小見出し", &dup, "-", 2) + assert.Equal(t, "小見出し", sl.GetPlainLine()) + assert.Equal(t, " - [小見出し](#小見出し)", sl.GetTocLine()) + + sl = NewSectionLineTestee(3, "小見出し4spaces", &dup, "-", 4) + assert.Equal(t, "小見出し4spaces", sl.GetPlainLine()) + assert.Equal(t, " - [小見出し4spaces](#小見出し4spaces)", sl.GetTocLine()) + + sl = NewSectionLineTestee(3, "asterisk mark", &dup, "*", 4) + assert.Equal(t, "asterisk mark", sl.GetPlainLine()) + assert.Equal(t, " * [asterisk mark](#asterisk-mark)", sl.GetTocLine(), "Test duplicated and including-space body") + assert.Equal(t, " * asterisk mark", sl.GetTocLineWithoutLinkformat()) +} + +func TestGetTocRange(t *testing.T) { + // origin0 := 0 + const origin1 = 1 + const notFound = -1 + thisOrigin := origin1 + + editTarget := "", // 3 + "", // 4 + "# Overview", // 5 + "intoc is a TOC Generator...", // 6 + "...", // 7 + "", // 8 + "# Feature", // 9 + } + yStart, yEnd := GetTocRange(testeeLinesNew, editTarget, listmark) + assert.Equal(t, thisOrigin+3, yStart, "onepass new case.") + assert.Equal(t, notFound, yEnd) + + testeeLinesOverwrite := []string{ + "# intoc", // 0 + "", // 1 + "", // 2 + "- [intoc](#intoc)", // 3 + " - [Overview](#overview)", // 4 + " - [Feature](#feature)", // 5 + " - [Install](#install)", // 6 + " - [Samples](#samples)", // 7 + " - [Basic](#basic)", // 8 + " - [Depth control](#depth-control)", // 9 + " - [License](#license)", // 10 + "", // 11 + "# Overview", // 12 + "intoc is a TOC Generator...", // 13 + "...", // 14 + "", // 15 + "# Feature", // 16 + } + yStart, yEnd = GetTocRange(testeeLinesOverwrite, editTarget, listmark) + assert.Equal(t, thisOrigin+2, yStart, "onepass overwrite case.") + assert.Equal(t, thisOrigin+10, yEnd) + + testeeLinesNotFound := []string{ + "# intoc", // 0 + "", // 1 + "## Table of contents", // 2 + "", // 3 + "# Overview", // 4 + "intoc is a TOC Generator...", // 5 + "...", // 6 + "", // 7 + "# Feature", // 8 + } + yStart, yEnd = GetTocRange(testeeLinesNotFound, editTarget, listmark) + assert.Equal(t, notFound, yStart, "onepass notfound case.") + assert.Equal(t, notFound, yEnd) +} + +func ____tocLines_and_outLines_tests____(){ +} + +const NoEditTargetPosition = -1 +const useDefaultGetting = true +var args = argparse(useDefaultGetting) + +var testdata string = `# title + +## sec1 +aaa + +### sec1-2 + +## せくしょん2 + +### セクション2-1 +### セクション2-2 +aiueo +### セクション2-3 +#### セクション2-3-1 +eof` + +var testdataForEdit string = `# title + +## sec1 +aaa + +### sec1-2 + +## table of contents + + +## せくしょん2 + +### セクション2-1 +### セクション2-2 +aiueo +### セクション2-3 +#### セクション2-3-1 +eof` + +var testdataForEditOverwrite string = `# title + +## sec1 +aaa + +### sec1-2 + +## table of contents + +- TOC + - already + - exists. + +## せくしょん2 + +### セクション2-1 +### セクション2-2 +aiueo +### セクション2-3 +#### セクション2-3-1 +eof` + +var expectForEdit string = `# title + +## sec1 +aaa + +### sec1-2 + +## table of contents + +- title + - sec1 + - sec1-2 + - table of contents + - せくしょん2 + - セクション2-1 + - セクション2-2 + - セクション2-3 + - セクション2-3-1 + +## せくしょん2 + +### セクション2-1 +### セクション2-2 +aiueo +### セクション2-3 +#### セクション2-3-1 +eof` + +var testdataForHilightInclusion string = `# including hilight syntax + +## python + +` + "```" + `py +# hello +print("hello world.") +` + "```" + ` + +## markdown + +` + "```" + ` +# level1 +## level2 +### level3 +- list + - list2 + - list3 +` + "```" + ` +` + +// PrepareDefaultPrameters prepare default parameters for outLines testing. +// This parameters do not mean intoc's default. +func PrepareDefaultPrameters(testdata string) ([]string, Duplicator) { + const useDefaultGetting = true + + lines := strings.Split(testdata, "\n") + duplicator := NewDuplicator() + ClearArgsParameter(&args) + + return lines, duplicator +} + +func ClearArgsParameter(args *Args) () { + *args.useAsterisk = false + *args.noLinkformat = false + *args.usePlainEnum = false + *args.useEdit = false + *args.editTarget = "