git » css3fmt » commit aa3417d

Initial commit

author Alberto Bertogli
2020-06-04 20:54:33 UTC
committer Alberto Bertogli
2020-06-04 21:08:07 UTC

Initial commit

.gitignore +3 -0
LICENSE +25 -0
README.md +51 -0
css3fmt.go +163 -0
go.mod +5 -0
go.sum +2 -0

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..89f2d6f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+css3fmt
+.*
+!.gitignore
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b68719a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+css3fmt is under the MIT licence, which is reproduced below (taken from
+http://opensource.org/licenses/MIT).
+
+-----
+
+Copyright (c)  Alberto Bertogli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1f46f77
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+
+# css3fmt
+
+[css3fmt](https://blitiri.com.ar/git/r/css3fmt) is an auto-formatter for
+[CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets) files.
+
+It is not particularly fancy or smart, but it is simple and can automatically
+format most CSS files.
+
+
+## Install
+
+css3fmt is written in Go.
+
+```sh
+go get blitiri.com.ar/go/css3fmt
+```
+
+
+## Editor integration
+
+### vim
+
+Put the following into your `.vimrc` file to auto-indent on save:
+
+```vim
+function! CSSFormatBuffer()
+        let l:curw = winsaveview()
+        let l:tmpname = tempname()
+        call writefile(getline(1,'$'), l:tmpname)
+        let l:out = system("css3fmt " . l:tmpname) 
+        call delete(l:tmpname)  
+        if v:shell_error == 0           
+                try | silent undojoin | catch | endtry
+                silent %!css3fmt     
+        else    
+                echoerr l:out
+        endif
+        call winrestview(l:curw)
+        return v:shell_error == 0
+endfunction
+autocmd filetype css
+  \ autocmd bufwritepre <buffer> call CSSFormatBuffer()
+```
+
+
+## Contact
+
+If you have any questions, comments or patches please send them to
+albertito@blitiri.com.ar.
+
diff --git a/css3fmt.go b/css3fmt.go
new file mode 100644
index 0000000..e9ad4c5
--- /dev/null
+++ b/css3fmt.go
@@ -0,0 +1,163 @@
+// css3fmt is an auto-indenter/formatter for CSS.
+//
+// See https://blitiri.com.ar/git/r/css3fmt for more details.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/gorilla/css/scanner"
+)
+
+var (
+	rewrite = flag.Bool("w", false,
+		"Do not print reformatted sources to standard output."+
+			" If a file's formatting is different from gofmt's,"+
+			" overwrite it with the formatted version")
+)
+
+func main() {
+	flag.Parse()
+
+	if args := flag.Args(); len(args) > 0 {
+		for _, fname := range args {
+			f, err := os.Open(fname)
+			if err != nil {
+				fatalf("%s: %s\n", fname, err)
+			}
+			defer f.Close()
+
+			s := indent(f)
+			if *rewrite {
+				err = ioutil.WriteFile(fname, []byte(s), 0660)
+				if err != nil {
+					fatalf("%s: %s\n", fname, err)
+				}
+			} else {
+				os.Stdout.WriteString(s)
+			}
+		}
+	} else {
+		os.Stdout.WriteString(indent(os.Stdin))
+	}
+}
+
+func fatalf(s string, a ...interface{}) {
+	fmt.Fprintf(os.Stderr, s, a...)
+	os.Exit(1)
+}
+
+func indent(f *os.File) string {
+	buf, err := ioutil.ReadAll(f)
+	if err != nil {
+		fatalf("%s: error reading: %s\n", f.Name(), err)
+	}
+
+	out := output{}
+
+	s := scanner.New(string(buf))
+scan:
+	for {
+		t := s.Next()
+		switch t.Type {
+		case scanner.TokenEOF:
+			break scan
+		case scanner.TokenError:
+			fatalf("%s:%d:%d: error tokenizing: %s\n",
+				f.Name(), t.Line, t.Column, t.Value)
+		case scanner.TokenChar:
+			switch t.Value {
+			case "{":
+				out.emit("{\n")
+				out.indent++
+			case "}":
+				out.indent--
+				out.emit("}\n")
+				if out.indent == 0 {
+					out.emit("\n")
+				}
+			case ";":
+				if out.inFunc {
+					out.inFunc = false
+					out.indent--
+				}
+				out.emit(";\n")
+			default:
+				out.emit(t.Value)
+			}
+		case scanner.TokenS:
+			if !strings.Contains(t.Value, "\n") {
+				out.emit(" ")
+			} else if out.inFunc {
+				// Respect newline within functions, as users know best how to
+				// break up arguments.
+				out.emit("\n")
+			}
+			out.afterEmptyLine = strings.Contains(t.Value, "\n\n")
+		case scanner.TokenComment:
+			out.emitComment(t.Value)
+		case scanner.TokenFunction:
+			out.emit(t.Value)
+			if !out.inFunc {
+				out.inFunc = true
+				out.indent++
+			}
+		default:
+			out.emit(t.Value)
+		}
+
+		//fmt.Printf("\n«%s»\n", t)
+	}
+
+	return strings.Trim(out.buf.String(), "\n") + "\n"
+}
+
+type output struct {
+	indent int
+
+	afterN         bool
+	afterEmptyLine bool
+	inFunc         bool
+
+	buf strings.Builder
+}
+
+func (o *output) emit(s string) {
+	// Indent if we just came from a newline, UNLESS we're only printing a
+	// newline, to avoid trailing spaces.
+	if o.afterN && s != "\n" {
+		for i := 0; i < o.indent; i++ {
+			//o.buf.WriteString("‧‧‧‧")
+			o.buf.WriteString("    ")
+		}
+	}
+
+	o.buf.WriteString(s)
+	o.afterN = strings.HasSuffix(s, "\n")
+	o.afterEmptyLine = false
+}
+
+func (o *output) emitComment(s string) {
+	// We preserve empty newlines before comments, so they can be used to
+	// break long series of entries, or to group sections.
+	if o.afterEmptyLine {
+		o.emit("\n")
+	}
+
+	// Emit the lines adjusting indentation for "* ".
+	lines := strings.Split(s, "\n")
+	for _, l := range lines {
+		if strings.HasPrefix(trimAllSp(l), "* ") {
+			l = " " + trimAllSp(l)
+		}
+		o.emit(l + "\n")
+	}
+}
+
+func trimAllSp(s string) string {
+	return strings.Trim(s, " \t\r\n")
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0543d44
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module blitiri.com.ar/go/css3fmt
+
+go 1.14
+
+require github.com/gorilla/css v1.0.0
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..6ed4c52
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=