commit 6c44b28a5f191d7d19f65a52fd594205f6c06239 Author: lordwelch Date: Thu May 3 13:51:49 2018 -0700 Formats witcher script files diff --git a/text/lex/lex.go b/text/lex/lex.go new file mode 100644 index 0000000..82a2f85 --- /dev/null +++ b/text/lex/lex.go @@ -0,0 +1,646 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lex + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +// Item represents a token or text string returned from the scanner. +type Item struct { + Typ ItemType // The type of this item. + Pos Pos // The starting position, in bytes, of this item in the input string. + Val string // The value of this item. +} + +func (i Item) String() string { + switch { + case i.Typ == ItemComment: + return fmt.Sprint(i.Val) + case i.Typ == ItemEOF: + return "EOF" + case i.Typ == ItemError: + return i.Val + case i.Typ == ItemModifiers: + case i.Typ > ItemKeyword: + return fmt.Sprintf("<%s>\t", i.Val) + } + return fmt.Sprintf("%q\t%s", i.Val, Rkey[i.Typ]) +} + +// ItemType identifies the type of lex items. +type ItemType int + +const ( + ItemError ItemType = iota // error occurred; value is text of error + ItemBool // boolean constant + ItemChar // printable ASCII character; grab bag for comma etc. + ItemCharConstant // character constant + ItemComplex // complex constant (1+2i); imaginary is just a number + ItemColonEquals // colon-equals (':=') introducing a declaration + ItemEOF + ItemField // alphanumeric identifier starting with '.' + ItemIdentifier // alphanumeric identifier not starting with '.' + ItemLeftDelim // left action delimiter + ItemLeftParen // '(' inside action + ItemNumber // simple number, including imaginary + ItemPipe // pipe symbol + ItemRawString // raw quoted string (includes quotes) + ItemRightDelim // right action delimiter + ItemRightParen // ')' inside action + ItemSpace // run of spaces separating arguments + ItemNewline // newline + ItemString // quoted string (includes quotes) + ItemText // plain text + ItemVariable // variable starting with '$', such as '$' or '$1' or '$hello' + ItemLeftBrace + ItemRightBrace + ItemComment + ItemOperator + // Keywords appear after all the rest. + ItemKeyword // used only to delimit the keywords + ItemDot // the cursor, spelled '.' + ItemDefine // define keyword + ItemElse // else keyword + ItemEnd // end keyword + ItemIf // if keyword + ItemNil // the untyped nil constant, easiest to treat as a keyword + ItemRange // range keyword + ItemTemplate // template keyword + ItemWith // with keyword + ItemFor // for keyword + ItemSwitch // switch keyword + ItemCase // case keyword + ItemWhile // while keyword + ItemReturn // return keyword + ItemBreak // break keyword + ItemContinue // continue keyword + ItemVar // var keyword + ItemEnum // enum keyword + ItemStruct // struct keyword + ItemFunction // function keyword + ItemEvent // event keyword + ItemClass // class keyword + ItemArray // array keyword + ItemModifiers +) + +var key = map[string]ItemType{ + ".": ItemDot, + "define": ItemDefine, + "else": ItemElse, + // "end": ItemEnd, + "if": ItemIf, + // "range": ItemRange, + // "nil": ItemNil, + // "template": ItemTemplate, + // "with": ItemWith, + "for": ItemFor, // ws keywords + "switch": ItemSwitch, + "case": ItemCase, + "while": ItemWhile, + "return": ItemReturn, + // "break": ItemBreak, + // "continue": ItemContinue, + "var": ItemVar, + "enum": ItemEnum, + "struct": ItemStruct, + "function": ItemFunction, + // "event": ItemEvent, + // "class": ItemClass, + "array": ItemArray, + "abstract": ItemModifiers, // ws modifiers + "entry": ItemModifiers, + "out": ItemModifiers, + "saved": ItemModifiers, + "storyscene": ItemModifiers, + "quest": ItemModifiers, + "exec": ItemModifiers, + "timer": ItemModifiers, + "final": ItemModifiers, + "import": ItemModifiers, + "const": ItemModifiers, + "editable": ItemModifiers, + "default": ItemModifiers, + "statemachine": ItemModifiers, + "private": ItemModifiers, + "protected": ItemModifiers, + "public": ItemModifiers, +} + +var Rkey = map[ItemType]string{ + ItemError: "error", + ItemBool: "bool", + ItemChar: "char", + ItemCharConstant: "charConstant", + ItemComplex: "complex", + ItemColonEquals: "colonEquals", + ItemEOF: "EOF", + ItemField: "field", + ItemIdentifier: "identifier", + ItemLeftDelim: "leftDelim", + ItemLeftParen: "leftParen", + ItemNumber: "number", + ItemPipe: "pipe", + ItemRawString: "rawString", + ItemRightDelim: "rightDelim", + ItemRightParen: "rightParen", + ItemSpace: "space", + ItemNewline: "newline", + ItemString: "string", + ItemText: "text", + ItemVariable: "variable", + ItemOperator: "operator", + ItemModifiers: "modifier", + ItemLeftBrace: "leftBrace", + ItemRightBrace: "rightBrace", +} + +const eof = -1 + +// Pos represents a byte position in the original input text from which +// this template was parsed. +type Pos int + +func (p Pos) Position() Pos { + return p +} + +// stateFn represents the state of the scanner as a function that returns the next state. +type stateFn func(*Lexer) stateFn + +// Lexer holds the state of the scanner. +type Lexer struct { + name string // the name of the input; used only for error reports + input string // the string being scanned + leftDelim string // start of action + rightDelim string // end of action + state stateFn // the next lexing function to enter + pos Pos // current position in the input + start Pos // start position of this item + width Pos // width of last rune read from input + lastPos Pos // position of most recent item returned by nextItem + items chan Item // channel of scanned items + parenDepth int // nesting depth of ( ) exprs + braceDepth int // nesting depth of { } +} + +// next returns the next rune in the input. +func (l *Lexer) next() rune { + if int(l.pos) >= len(l.input) { + l.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(l.input[l.pos:]) + l.width = Pos(w) + l.pos += l.width + return r +} + +// peek returns but does not consume the next rune in the input. +func (l *Lexer) peek() rune { + if int(l.pos) >= len(l.input) { + return eof + } + r, _ := utf8.DecodeRuneInString(l.input[l.pos:]) + return r +} + +// backup steps back one rune. Can only be called once per call of next. +func (l *Lexer) backup() { + l.pos -= l.width +} + +// emit passes an item back to the client. +func (l *Lexer) emit(t ItemType) { + l.items <- Item{t, l.start, l.input[l.start:l.pos]} + l.start = l.pos +} + +// ignore skips over the pending input before this point. +func (l *Lexer) ignore() { + l.start = l.pos +} + +// accept consumes the next rune if it's from the valid set. +func (l *Lexer) accept(valid string) bool { + if strings.IndexRune(valid, l.next()) >= 0 { + return true + } + l.backup() + return false +} + +// acceptRun consumes a run of runes from the valid set. +func (l *Lexer) acceptRun(valid string) { + for strings.IndexRune(valid, l.next()) >= 0 { + } + l.backup() +} + +// lineNumber reports which line we're on, based on the position of +// the previous item returned by nextItem. Doing it this way +// means we don't have to worry about peek double counting. +func (l *Lexer) lineNumber() int { + return 1 + strings.Count(l.input[:l.lastPos], "\n") +} + +// errorf returns an error token and terminates the scan by passing +// back a nil pointer that will be the next state, terminating l.nextItem. +func (l *Lexer) errorf(format string, args ...interface{}) stateFn { + l.items <- Item{ItemError, l.start, fmt.Sprintf(format, args...)} + return nil +} + +// nextItem returns the next item from the input. +// Called by the parser, not in the lexing goroutine. +func (l *Lexer) NextItem() Item { + item := <-l.items + l.lastPos = item.Pos + return item +} + +// drain drains the output so the lexing goroutine will exit. +// Called by the parser, not in the lexing goroutine. +func (l *Lexer) drain() { + for range l.items { + } +} + +// lex creates a new scanner for the input string. +func Lex(name, input string) *Lexer { + l := &Lexer{ + name: name, + input: input, + items: make(chan Item), + } + go l.run() + return l +} + +// run runs the state machine for the lexer. +func (l *Lexer) run() { + for l.state = lexInsideAction; l.state != nil; { + l.state = l.state(l) + } + close(l.items) +} + +// state functions + +const ( + leftComment = "/*" + rightComment = "*/" +) + +// lexComment scans a comment. The left comment marker is known to be present. +func lexComment(l *Lexer) stateFn { + l.pos += Pos(len(leftComment)) + i := strings.Index(l.input[l.pos:], rightComment) + if i < 0 { + return l.errorf("unclosed comment") + } + l.pos += Pos(i + len(rightComment)) + /*if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) { + return l.errorf("comment ends before closing delimiter") + + } + l.pos += Pos(len(l.rightDelim))*/ + l.emit(ItemComment) + return lexInsideAction +} + +// lexSingleLineComment scans until the end of the line +func lexSingleLineComment(l *Lexer) stateFn { + i := strings.Index(l.input[l.pos:], "\n") + if i < 0 { + l.pos = Pos(len(l.input)) + } + l.pos += Pos(i) + + l.emit(ItemComment) + return lexInsideAction +} + +// lexInsideAction scans the elements inside action delimiters. +func lexInsideAction(l *Lexer) stateFn { + // Either number, quoted string, or identifier. + // Spaces separate arguments; runs of spaces turn into ItemSpace. + // Pipe symbols separate and are emitted. + switch r := l.next(); { + case r == eof: + if l.parenDepth != 0 { + return l.errorf("unclosed left paren") + } + if l.braceDepth != 0 { + return l.errorf("unclosed left paren") + } + l.emit(ItemEOF) + return nil + case isEndOfLine(r): + return lexEOL + case isSpace(r): + return lexSpace + case strings.HasPrefix(l.input[l.pos-l.width:], leftComment): + return lexComment + case strings.HasPrefix(l.input[l.pos-l.width:], "//"): + return lexSingleLineComment + case r == '"': + return lexQuote + case r == '`': + return lexRawQuote + case r == '$': + return lexVariable + case r == '\'': + return lexChar + case r == '.': + // special look-ahead for ".field" so we don't break l.backup(). + r = l.peek() + if r < '0' || '9' < r { + l.emit(ItemChar) + return lexInsideAction + } + fallthrough // '.' can start a number. + case r == '+' || r == '-' || ('0' <= r && r <= '9'): + if r == '+' || r == '-' { + r := l.peek() + if (r < '0' || '9' < r) && r != '.' { + return lexOperator + } + } + + l.backup() + return lexNumber + case isOperator(r): + return lexOperator + case isAlphaNumeric(r): + l.backup() + return lexIdentifier + case r == '(': + l.emit(ItemLeftParen) + l.parenDepth++ + case r == ')': + l.emit(ItemRightParen) + l.parenDepth-- + if l.parenDepth < 0 { + return l.errorf("unexpected right paren %#U", r) + } + case r == '{': + l.emit(ItemLeftBrace) + l.braceDepth++ + case r == '}': + l.emit(ItemRightBrace) + l.braceDepth-- + if l.braceDepth < 0 { + return l.errorf("unexpected right brace %#U", r) + } + case r <= unicode.MaxASCII && unicode.IsPrint(r): + l.emit(ItemChar) + return lexInsideAction + default: + return l.errorf("unrecognized character in action: %#U", r) + } + return lexInsideAction +} + +func lexOperator(l *Lexer) stateFn { + l.acceptRun("%&*/!+=-|") + l.emit(ItemOperator) + return lexInsideAction +} + +// lexEOL scans a run of end of line characters. +// One character has already been seen. +func lexEOL(l *Lexer) stateFn { + for isEndOfLine(l.peek()) { + l.next() + } + l.emit(ItemNewline) + return lexInsideAction +} + +// lexSpace scans a run of space characters. +// One space has already been seen. +func lexSpace(l *Lexer) stateFn { + for isSpace(l.peek()) { + l.next() + } + l.emit(ItemSpace) + return lexInsideAction +} + +// lexIdentifier scans an alphanumeric. +func lexIdentifier(l *Lexer) stateFn { +Loop: + for { + switch r := l.next(); { + case isAlphaNumeric(r) || r == '.': + // absorb. + default: + l.backup() + word := l.input[l.start:l.pos] + if !l.atTerminator() { + return l.errorf("bad character %#U", r) + } + switch { + case key[word] > ItemKeyword: + l.emit(key[word]) + case word[0] == '.': + l.emit(ItemField) + case word == "true", word == "false": + l.emit(ItemBool) + default: + l.emit(ItemIdentifier) + } + break Loop + } + } + return lexInsideAction +} + +// lexField scans a field: .Alphanumeric. +// The . has been scanned. +func lexField(l *Lexer) stateFn { + return lexFieldOrVariable(l, ItemField) +} + +// lexVariable scans a Variable: $Alphanumeric. +// The $ has been scanned. +func lexVariable(l *Lexer) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "$". + l.emit(ItemVariable) + return lexInsideAction + } + return lexFieldOrVariable(l, ItemVariable) +} + +// lexVariable scans a field or variable: [.$]Alphanumeric. +// The . or $ has been scanned. +func lexFieldOrVariable(l *Lexer, typ ItemType) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "." or "$". + if typ == ItemVariable { + l.emit(ItemVariable) + } else { + l.emit(ItemDot) + } + return lexInsideAction + } + var r rune + for { + r = l.next() + if !isAlphaNumeric(r) { + l.backup() + break + } + } + if !l.atTerminator() { + return l.errorf("bad character %#U", r) + } + l.emit(typ) + return lexInsideAction +} + +// atTerminator reports whether the input is at valid termination character to +// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases +// like "$x+2" not being acceptable without a space, in case we decide one +// day to implement arithmetic. +func (l *Lexer) atTerminator() bool { + r := l.peek() + if isSpace(r) || isEndOfLine(r) { + return true + } + switch r { + case eof, '.', ',', '|', ':', ')', '(', ';', '[', ']', '?', '{': + return true + } + if isOperator(r) { + return true + } + // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will + // succeed but should fail) but only in extremely rare cases caused by willfully + // bad choice of delimiter. + if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r { + return true + } + return false +} + +// lexChar scans a character constant. The initial quote is already +// scanned. Syntax checking is done by the parser. +func lexChar(l *Lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated character constant") + case '\'': + break Loop + } + } + l.emit(ItemString) + // l.emit(ItemCharConstant) + return lexInsideAction +} + +// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This +// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" +// and "089" - but when it's wrong the input is invalid and the parser (via +// strconv) will notice. +func lexNumber(l *Lexer) stateFn { + if !l.scanNumber() { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } // complex number logic removed. Messes with math operations without space + l.emit(ItemNumber) + + return lexInsideAction +} + +func (l *Lexer) scanNumber() bool { + // Optional leading sign. + l.accept("+-") + // Is it hex? + digits := "0123456789" + if l.accept("0") && l.accept("xX") { + digits = "0123456789abcdefABCDEF" + } + l.acceptRun(digits) + if l.accept(".") { + l.acceptRun(digits) + } + if l.accept("eE") { + l.accept("+-") + l.acceptRun("0123456789") + } + // Is it imaginary? + l.accept("if") // ws alows f after a float + // Next thing mustn't be alphanumeric. + if isAlphaNumeric(l.peek()) { + l.next() + return false + } + return true +} + +// lexQuote scans a quoted string. +func lexQuote(l *Lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated quoted string") + case '"': + break Loop + } + } + l.emit(ItemString) + return lexInsideAction +} + +// lexRawQuote scans a raw quoted string. +func lexRawQuote(l *Lexer) stateFn { +Loop: + for { + switch l.next() { + case eof: + return l.errorf("unterminated raw quoted string") + case '`': + break Loop + } + } + l.emit(ItemRawString) + return lexInsideAction +} + +// isSpace reports whether r is a space character. +func isSpace(r rune) bool { + return r == ' ' || r == '\t' +} + +// isEndOfLine reports whether r is an end-of-line character. +func isEndOfLine(r rune) bool { + return r == '\r' || r == '\n' +} + +// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. +func isAlphaNumeric(r rune) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} + +func isOperator(r rune) bool { + return strings.IndexRune("%&*/!+=-|<>", r) >= 0 +} diff --git a/wsfmt.go b/wsfmt.go new file mode 100644 index 0000000..c16e54d --- /dev/null +++ b/wsfmt.go @@ -0,0 +1,554 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" + "timmy.narnian.us/git/timmy/wsfmt/text/lex" +) + +type stateFn func(*Formatter) stateFn + +type FmtSettings struct{} + +type Formatter struct { + l *lex.Lexer + pToken lex.Item + token lex.Item + nToken lex.Item + state stateFn + maxNewlines int + newlineCount int + scopeLevel []int + tempScopeLevel int + eol, CASE bool + parenDepth int +} + +var ( + blank = lex.Item{Pos: -1} + b []byte + err error +) + +func main() { + file, _ := os.Open(os.Args[1]) + f := Format(nil, file) + f.run() +} + +func Format(typ *FmtSettings, text io.Reader) (f *Formatter) { + FILE := transform.NewReader(text, unicode.BOMOverride(unicode.UTF8.NewDecoder().Transformer)) + b, err = ioutil.ReadAll(FILE) + if err != nil { + panic(err) + } + f = &Formatter{} + f.l = lex.Lex("name", string(b)) + f.maxNewlines = 3 + f.nToken = blank + return f +} + +func (f *Formatter) next() lex.Item { + f.pToken = f.token + var temp lex.Item + + if f.nToken == blank { + temp = f.l.NextItem() + } else { + temp = f.nToken + f.nToken = blank + } + for ; temp.Typ == lex.ItemSpace || temp.Typ == lex.ItemNewline; temp = f.l.NextItem() { + } + + f.token = temp + return f.token +} + +func (f *Formatter) peek() lex.Item { + if f.nToken == blank { + temp := f.l.NextItem() + count := 0 + for ; temp.Typ == lex.ItemSpace || temp.Typ == lex.ItemNewline; temp = f.l.NextItem() { + if temp.Typ == lex.ItemNewline { + count += strings.Count(temp.Val, "\n") + } + } + if count < 3 { + f.newlineCount = count + } else { + f.newlineCount = 3 + } + f.nToken = temp + } + return f.nToken +} + +func (f *Formatter) run() { + for f.state = format; f.state != nil; { + f.state = f.state(f) + } +} + +func format(f *Formatter) stateFn { + switch t := f.next().Typ; { + case t == lex.ItemEOF: + return nil + case t == lex.ItemError: + fmt.Print("error:", f.token.Val) + return nil + case t == lex.ItemComment: + f.printComment() + case t == lex.ItemFunction: + return formatFunction + case t == lex.ItemIf, t == lex.ItemWhile, t == lex.ItemFor, t == lex.ItemSwitch: + return formatConditional + case t == lex.ItemElse: + if f.pToken.Typ == lex.ItemRightBrace { + fmt.Printf(" else") + } else { + fmt.Print("else") + } + if f.peek().Typ != lex.ItemLeftBrace && f.peek().Typ != lex.ItemIf { + f.scopeLevel[len(f.scopeLevel)-1]++ + printNewline(f) + printTab(f) + } + case t == lex.ItemReturn: + fmt.Printf("%s ", f.token.Val) + case t == lex.ItemModifiers, t == lex.ItemIdentifier, t == lex.ItemNumber, t == lex.ItemBool, t == lex.ItemString: + printIdentifier(f) + case isChar(t): + return printChar(f) + case t == lex.ItemStruct: + return formatStruct + case t == lex.ItemVar: + return formatVar + case t == lex.ItemOperator: + printOperator(f) + case t == lex.ItemArray: + if !printArray(f) { + return nil + } + case t == lex.ItemCase: + return formatCase + case t == lex.ItemEnum: + return formatEnum + default: + fmt.Fprintf(os.Stderr, "\nexpected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + return format +} + +func (f *Formatter) printComment() { + fmt.Printf("%s", f.token.Val) + printNewline(f) +} + +func formatFunction(f *Formatter) stateFn { + if f.token.Typ == lex.ItemFunction { + fmt.Printf("%s ", f.token.Val) + } + + switch t := f.next().Typ; { + case t == lex.ItemEOF: + fmt.Fprintf(os.Stderr, "unexpected EOF wanted identifier\n") + return nil + case t == lex.ItemComment: + f.printComment() + case t == lex.ItemIdentifier: + printIdentifier(f) + if f.next().Typ == lex.ItemLeftParen { + printChar(f) + return format + } + } + fmt.Print("\n") + fmt.Fprintf(os.Stderr, "\nexpected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + // return formatFunction +} + +func formatStruct(f *Formatter) stateFn { + if f.token.Typ == lex.ItemStruct { + printIdentifier(f) + } + switch t := f.next().Typ; { + case t == lex.ItemEOF: + fmt.Fprintf(os.Stderr, "unexpected EOF wanted identifier\n") + return nil + case t == lex.ItemComment: + f.printComment() + case t == lex.ItemIdentifier: + printIdentifier(f) + return format + default: + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + return formatStruct +} + +func formatVar(f *Formatter) stateFn { + printIdentifier(f) + for notdone := true; notdone; { + if f.next().Typ == lex.ItemIdentifier { + printIdentifier(f) + if f.next().Typ == lex.ItemChar { + switch f.token.Val { + case ",": + printChar(f) + case ":": + printChar(f) + notdone = false + default: + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + } else { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + } else { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + } + switch f.next().Typ { + case lex.ItemIdentifier: + printIdentifier(f) + case lex.ItemArray: + if !printArray(f) { + return nil + } + default: + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + return format +} + +func printIdentifier(f *Formatter) { + str := "%s" + switch f.peek().Val { + case "{", "}", "(", ")", "[", "]", "|", ",", ":", ";", ".": + default: + str += " " + } + fmt.Printf(str, f.token.Val) +} + +func printOperator(f *Formatter) { + str := "%s" + switch f.token.Val { + case "|", "!": + case "+", "-": + switch f.pToken.Typ { + case lex.ItemLeftParen, lex.ItemOperator, lex.ItemReturn: + default: + str += " " + } + default: + str += " " + } + switch f.pToken.Val { + case ")", "]": + str = " " + str + } + fmt.Printf(str, f.token.Val) +} + +func formatConditional(f *Formatter) stateFn { + switch f.token.Typ { + case lex.ItemIf, lex.ItemWhile, lex.ItemFor, lex.ItemSwitch: + tok := f.token.Val + if f.pToken.Typ == lex.ItemElse { + fmt.Print(" ") + } + if f.next().Typ != lex.ItemLeftParen { + fmt.Fprintf(os.Stderr, "expected parenthesis got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + fmt.Printf("%s (", tok) + f.parenDepth = 1 + } + + switch t := f.next().Typ; { + case t == lex.ItemEOF: + fmt.Fprintf(os.Stderr, "unexpected EOF wanted identifier\n") + return nil + case t == lex.ItemComment: + f.printComment() + case t == lex.ItemOperator: + printOperator(f) + case t == lex.ItemIdentifier, t == lex.ItemNumber, t == lex.ItemString, t == lex.ItemBool: + printIdentifier(f) + case isChar(t): + if f.token.Val == ";" { + fmt.Print("; ") + } else { + printChar(f) + } + switch f.token.Val { + case ")": + f.parenDepth-- + if f.parenDepth == 0 { + if f.peek().Typ != lex.ItemLeftBrace { + f.scopeLevel[len(f.scopeLevel)-1]++ + printNewline(f) + printTab(f) + } + return format + } + case "(": + f.parenDepth++ + } + } + return formatConditional +} + +func formatNewLine(f *Formatter) stateFn { + printNewline(f) + + switch t := f.peek().Typ; { + case t == lex.ItemEOF: + return nil + case t == lex.ItemError: + fmt.Print("error:", f.token.Val) + return nil + case t == lex.ItemCase: + f.scopeLevel = f.scopeLevel[:len(f.scopeLevel)-1] + printTab(f) + case isChar(t): + switch f.nToken.Val { + case ":", ",": + fmt.Printf("%s ", f.token.Val) + case ";": + fmt.Print(f.token.Val) + f.next() + fmt.Print(f.token.Val) + if len(f.scopeLevel) > 0 { + f.scopeLevel[len(f.scopeLevel)-1] = 1 + } + case "}": + // f.scopeLevel = f.scopeLevel[:len(f.scopeLevel)-1] + // printTab(f) + // fmt.Print("}") + f.next() + f.peek() + return formatRightBrace + default: + printTab(f) + } + return format + default: + printTab(f) + } + + return format +} + +func formatEnum(f *Formatter) stateFn { + printIdentifier(f) + if f.next().Typ != lex.ItemIdentifier { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + printIdentifier(f) + if f.next().Typ != lex.ItemLeftBrace { + fmt.Fprintf(os.Stderr, "expected left brace got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + + f.scopeLevel = append(f.scopeLevel, 1) + fmt.Print(" {") + f.newlineCount = -1 + printNewline(f) + printTab(f) + return formatEnumIdent +} + +func formatEnumIdent(f *Formatter) stateFn { + if f.next().Typ != lex.ItemIdentifier { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + printIdentifier(f) + switch f.peek().Val { + case "=": + f.next() + printOperator(f) + if f.peek().Typ != lex.ItemNumber { + fmt.Fprintf(os.Stderr, "expected Number got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + f.next() + printIdentifier(f) + if f.peek().Typ == lex.ItemRightBrace { + return format + } + case "}": + return format + } + return formatEnumChar +} + +func formatEnumChar(f *Formatter) stateFn { + if f.next().Val != "," { + fmt.Fprintf(os.Stderr, "expected Comma got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return nil + } + + fmt.Print(",") + if f.peek().Typ == lex.ItemRightBrace { + return format + } + printNewline(f) + printTab(f) + return formatEnumIdent +} + +func formatRightBrace(f *Formatter) stateFn { + f.scopeLevel = f.scopeLevel[:len(f.scopeLevel)-1] + f.newlineCount = -1 + printNewline(f) + printTab(f) + fmt.Print("}") + + switch f.peek().Typ { + case lex.ItemChar, lex.ItemElse, lex.ItemRightBrace: + return format + } + + return formatNewLine +} + +func formatCase(f *Formatter) stateFn { + printIdentifier(f) + if !printCase(f) { + return nil + } + + if f.next().Val != ":" { + fmt.Fprintf(os.Stderr, "\nexpected \":\" got %s: %s %s\n", lex.Rkey[f.token.Typ], f.token.Val, f.pToken.Val) + return nil + } + fmt.Print(":") + f.scopeLevel = append(f.scopeLevel, 1) + return formatNewLine +} + +func printCase(f *Formatter) bool { + switch f.next().Typ { + case lex.ItemLeftParen: + fmt.Print(" (") + if f.next().Typ != lex.ItemIdentifier { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + printIdentifier(f) + if f.next().Typ != lex.ItemRightParen { + fmt.Fprintf(os.Stderr, "expected parenthesis got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + fmt.Print(")") + if f.next().Typ != lex.ItemIdentifier { + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + printIdentifier(f) + case lex.ItemIdentifier, lex.ItemNumber, lex.ItemString: + printIdentifier(f) + } + return true +} + +func printArray(f *Formatter) bool { + if f.next().Val != "<" { + fmt.Fprintf(os.Stderr, "expected \"<\" got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + fmt.Printf("array<") + switch f.next().Typ { + case lex.ItemIdentifier: + fmt.Print(f.token.Val) + case lex.ItemArray: + printArray(f) + default: + fmt.Fprintf(os.Stderr, "expected Identifier got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + if f.next().Val != ">" { + fmt.Fprintf(os.Stderr, "expected \">\" got %s: %s\n", lex.Rkey[f.token.Typ], f.token.Val) + return false + } + fmt.Print(">") + return true +} + +func printTab(f *Formatter) { + // fmt.Print(f.scopeLevel) + for _, t := range f.scopeLevel { + for i := 0; i < t; i++ { + fmt.Print("\t") + } + } +} + +func isChar(t lex.ItemType) bool { + switch t { + case lex.ItemChar, lex.ItemLeftParen, lex.ItemRightParen, lex.ItemLeftBrace, lex.ItemRightBrace: + return true + default: + return false + } +} + +func printChar(f *Formatter) stateFn { + switch f.token.Val { + case ":", ",": + fmt.Printf("%s ", f.token.Val) + case ";": + fmt.Print(";") + if len(f.scopeLevel) > 0 { + f.scopeLevel[len(f.scopeLevel)-1] = 1 + } + return formatNewLine + case "{": + fmt.Print(" {") + f.scopeLevel = append(f.scopeLevel, 1) + f.newlineCount = -1 + return formatNewLine + case "}": + return formatRightBrace + default: + fmt.Print(f.token.Val) + } + return format +} + +func printNewline(f *Formatter) { + if f.newlineCount == -1 { + f.peek() + if f.newlineCount < 1 { + f.newlineCount = 1 + } + } + f.peek() + if f.nToken.Typ != lex.ItemEOF { + for i := 0; i < f.newlineCount-1; i++ { + fmt.Print("\n") + } + fmt.Print("\n") + } else { + fmt.Print("\n") + } +} diff --git a/wsfmt.py b/wsfmt.py new file mode 100644 index 0000000..f06b5e5 --- /dev/null +++ b/wsfmt.py @@ -0,0 +1,61 @@ +import chardet +import string + + +class Parser: + wordFunc = { + "if": "wordif" + } + charFunc = { + "/": "comment" + } + wordBoundary = [ + "{", "}", "<", ">", "(", ")", "[", "]", + ";", ":", '"', "'", "=", "!", "-", "+", "/", "*", ",", "%" + ].extend(string.whitespace) + + def __init__(self, FileName: str): + self.indent = 0 + self.fmt = "" + self.currentWord = "" + self.wordIndex = 0 + self.file = self.openfile(FileName) + self.possibleLineComment = False + self.multiLineComment = False + + def openfile(FileName: str) -> str: + f = open(FileName, mode='b') + bytestr = f.read(10) + enc = chardet.detect(bytestr) + f.close() + + return open(FileName, encoding=enc['encoding'], buffering=1) + + def getNextWord(self): + cont = True + word = "" + ignorewhitespace = True + while cont: + char = self.file.read(1) + if not ignorewhitespace: + word += char + else: + if char not in string.whitespace: + ignorewhitespace = False + word += char + + if char in self.wordBoundary and not ignorewhitespace: + cont = False + self.currentWord = word.strip() + + def parseWord(self): + + def parse(self): + cont = True + while cont: + self.getNextWord() + self.parseWord() + + +wsparser = Parser("test.ws") +wsparser.parse() diff --git a/wsfmt.sublime-project b/wsfmt.sublime-project new file mode 100644 index 0000000..24db303 --- /dev/null +++ b/wsfmt.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "." + } + ] +} diff --git a/wsfmt.sublime-workspace b/wsfmt.sublime-workspace new file mode 100644 index 0000000..6d4a6e4 --- /dev/null +++ b/wsfmt.sublime-workspace @@ -0,0 +1,671 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "n", + "newlineCount\tint ν" + ], + [ + "pr", + "printIdentifier\t ƒ" + ], + [ + "ItemR", + "ItemRightParen\t Ɩ" + ], + [ + "st", + "stateFn\tfunc(*Formatter) stateFn ʈ" + ], + [ + "fun", + "func (*Formatter)\tfunc (...) {...} ʂ" + ], + [ + "pri", + "printIdentifier\t ƒ" + ], + [ + "for", + "formatEnumIdent\tstateFn ƒ" + ], + [ + "s", + "stateFn {}\tfunc() {...} ʂ" + ], + [ + "ItemEn", + "ItemEnum\t Ɩ" + ], + [ + "nex", + "next\tlex.Item ƒ" + ], + [ + "fo", + "format\tstateFn ƒ" + ], + [ + "r", + "ItemRightBrace\t Ɩ" + ], + [ + "ItemC", + "ItemCase\t Ɩ" + ], + [ + "P", + "printnewline\t ƒ" + ], + [ + "prnt", + "printTab\t ƒ" + ], + [ + "b", + "ItemBreak\t Ɩ" + ], + [ + "p", + "peek\tlex.Item ƒ" + ], + [ + "t", + "token\tlex.Item ν" + ], + [ + "pa", + "parenDepth\tint ν" + ], + [ + "prin", + "printIdentifier\t ƒ" + ], + [ + "l", + "ItemLeftParen\t Ɩ" + ], + [ + "f", + "format\tstateFn ƒ" + ], + [ + "le", + "ItemLeftParen\t Ɩ" + ], + [ + "std", + "Stdout\t*os.File ν" + ], + [ + "func", + "func (*stateFn)\tfunc (...) {...} ʂ" + ], + [ + "Itemst", + "ItemStruct\t Ɩ" + ], + [ + "ne", + "newlineCount\t[]int ν" + ], + [ + "Itemri", + "ItemRightBrace\t Ɩ" + ], + [ + "eo", + "eol\tbool ν" + ], + [ + "C", + "Count\tint ƒ" + ], + [ + "print", + "printNewline\t ƒ" + ], + [ + "ri", + "ItemRightBrace\t Ɩ" + ], + [ + "fu", + "func (*Formatter)\tfunc (...) {...} ʂ" + ], + [ + "ItemT", + "ItemType\tint ʈ" + ], + [ + "If", + "ItemIf\t Ɩ" + ], + [ + "if", + "ItemIf\t Ɩ" + ], + [ + "T", + "token\tlex.Item ν" + ], + [ + "i", + "ItemFunction\t Ɩ" + ], + [ + "ItemE", + "ItemError\t Ɩ" + ], + [ + "sta", + "stateFn {}\tfunc() {...} ʂ" + ], + [ + "I", + "ItemComment\t Ɩ" + ], + [ + "stateFn", + "stateFn {}\tfunc() {...} ʂ" + ], + [ + "ide", + "ItemIdentifier\t Ɩ" + ], + [ + "S", + "Sprint\tstring ƒ" + ], + [ + "ItemP", + "ItemLeftParen\t Ɩ" + ], + [ + "ItemF", + "ItemFunction\t Ɩ" + ], + [ + "For", + "Formatter\tstruct ʈ" + ], + [ + "D", + "DecodeBytes\tfunc(b []byte, val interface{}) error ·ƒ" + ], + [ + "M", + "MetaTorrent\tstruct ·ʈ" + ], + [ + "Me", + "metaTorrent\tstruct ·ʈ" + ], + [ + "curr", + "CurrentTorrents\tmap[string]*tf.SeriesTorrent ν" + ], + [ + "stri", + "strings\t package ρ" + ], + [ + "U", + "UploadRatio\tfloat64 ν" + ], + [ + "env", + "environ" + ], + [ + "File", + "FileInfo\tinterface ¡" + ], + [ + "Is", + "IsNotExist\tbool ƒ" + ], + [ + "Fi", + "Filename\tstring ν" + ], + [ + "e", + "Err\terror ƒ" + ], + [ + "clo", + "clobber\tbool ν" + ], + [ + "do", + "downloadFIle\t ƒ" + ], + [ + "filep", + "filepath\t package ρ" + ], + [ + "fil", + "FilePath\tstring ν" + ], + [ + "get", + "getDownloadFilename\t ƒ" + ], + [ + "cl", + "clobber\tbool ν" + ], + [ + "tor", + "torrentName\tstring ν" + ], + [ + "cur", + "CurrentTorrents\tmap[string]*tf.SeriesTorrent ν" + ], + [ + "to", + "torrentName\tstring ν" + ], + [ + "h", + "hash\tstring ν" + ], + [ + "a", + "arg\t package ρ" + ], + [ + "str", + "string\tbuilt-in ʈ" + ], + [ + "L", + "ToLower\tstring ƒ" + ], + [ + "Cur", + "CurrentTorrents\tmap[string]*tf.SeriesTorrent ν" + ], + [ + "E", + "Exit\t ƒ" + ], + [ + "Std", + "Stdin\t*os.File ν" + ], + [ + "torr", + "torrentPath\tstring ν" + ] + ] + }, + "buffers": + [ + { + "file": "wsfmt.go", + "settings": + { + "buffer_size": 12155, + "encoding": "UTF-8", + "line_ending": "Unix" + } + } + ], + "build_system": "", + "build_system_choices": + [ + ], + "build_varint": "", + "command_palette": + { + "height": 392.0, + "last_filter": "inst", + "selected_items": + [ + [ + "inst", + "Package Control: Install Package" + ], + [ + "GoSublime: err", + "GoSublime: Open Error Log" + ], + [ + "GoSublime: er", + "GoSublime: Open Error Log" + ], + [ + "control:", + "Package Control: Install Package" + ], + [ + "GoSublime: ", + "GoSublime: Restart Margo" + ], + [ + "rem", + "GoSublime: Add/Remove package" + ], + [ + "package control: i", + "Package Control: Install Package" + ], + [ + "package control:", + "Package Control: Remove Package" + ], + [ + "GoSublime: e", + "GoSublime: Show errors" + ] + ], + "width": 497.0 + }, + "console": + { + "height": 342.0, + "history": + [ + "print(os.environ['PATH'])", + "import os", + "print(os.environ['PATH'])", + "env" + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "expanded_folders": + [ + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt" + ], + "file_history": + [ + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/wsfmt.sublime-workspace", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/script.sublime-workspace", + "/home/timmy/.config/sublime-text-3/Packages/GoSublime/CHANGELOG.md", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/text/lex/lex.go", + "/usr/lib/go/src/strings/strings_amd64.go", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/scripts/core/object.ws", + "/home/timmy/.config/sublime-text-3/Packages/GoSublime/Default (Linux).sublime-keymap", + "/home/timmy/.config/sublime-text-3/Packages/GoSublime/GoSublime.sublime-settings", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/wsfmt.sublime-project", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/wsfmt", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/wsfmt.go", + "/home/timmy/.config/sublime-text-3/Packages/User/GoSublime.sublime-settings", + "/home/timmy/.config/sublime-text-3/Packages/User/GoSublime/linux-x64/log.txt", + "/home/timmy/.config/sublime-text-3/Packages/GoRename/Default (Linux).sublime-keymap", + "/home/timmy/.config/sublime-text-3/Packages/User/GoRename.sublime-settings", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt/scripts/engine/entity.ws", + "/home/timmy/.config/sublime-text-3/Packages/User/GoSublime/src/margo/margo.go", + "/home/timmy/build/bin/gogrepo.py.new", + "/home/timmy/build/bin/gogrepo.py", + "/home/timmy/GO/src/github.com/lordwelch/test/main.go", + "/home/timmy/GO/src/github.com/lordwelch/transmission/client.go", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/TorrentFilter/torrentproject.sublime-project", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/TorrentFilter/type.go", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/TorrentFilter/download/main.go", + "/home/timmy/GO/src/github.com/lordwelch/test/pkg/tst.go", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/scene/scene.go", + "/home/timmy/GO/src/github.com/lordwelch/transmission/torrent.go", + "/usr/lib/go/src/net/net.go", + "/home/timmy/.config/sublime-text-3/Packages/User/GoGuru.sublime-settings", + "/home/timmy/.config/sublime-text-3/Packages/User/Package Control.sublime-settings", + "/home/timmy/.config/sublime-text-3/Packages/GoRename/Default.sublime-settings", + "/home/timmy/.config/sublime-text-3/Packages/Package Control/Package Control.sublime-settings", + "/usr/lib/go/src/path/filepath/path.go", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/Default (Linux).sublime-mousemap", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/Default (Linux).sublime-keymap", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/Default.sublime-settings", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/dep/shellenv/_posix.py", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/dep/shellenv/_linux/__init__.py", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/goGuru.py", + "/home/timmy/.config/sublime-text-3/Packages/GoGuru/dep/shellenv/__init__.py", + "/usr/lib/go/src/os/file.go", + "/usr/lib/go/src/os/dir.go", + "/usr/lib/go/src/os/file_unix.go", + "/usr/lib/go/src/os/file_windows.go", + "/usr/lib/go/src/os/types.go", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/TorrentFilter/cmd/main.go", + "/usr/lib/go/src/os/stat_unix.go", + "/home/timmy/GO/src/github.com/cavaliercoder/grab/grab.go", + "/home/timmy/GO/src/github.com/cavaliercoder/grab/request.go", + "/usr/lib/go/src/net/http/doc.go", + "/usr/lib/go/src/net/http/request.go", + "/home/timmy/GO/src/github.com/cavaliercoder/grab/client.go", + "/usr/lib/go/src/strconv/itoa.go", + "/usr/lib/go/src/os/error.go", + "/usr/lib/go/src/os/error_unix.go", + "/usr/lib/go/src/os/error_windows.go", + "/home/timmy/.config/sublime-text-3/Packages/GoSublime/README.md", + "/home/timmy/GO/src/timmy.narnian.us/git/timmy/TorrentFilter/download/download", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/qml.go", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/main.go", + "/home/timmy/GO/src/gopkg.in/gographics/imagick.v2/imagick/magick_wand_env.go", + "/home/timmy/GO/src/github.com/limetext/qml-go/qmlobject.go", + "/home/timmy/GO/src/github.com/limetext/qml-go/qmlwindow.go", + "/usr/lib/go/src/builtin/builtin.go", + "/home/timmy/.config/sublime-text-3/Packages/GoSublime/9o.md", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/qrc.go", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/imagick.go", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/glfw.go", + "/home/timmy/GO/src/github.com/lordwelch/PresentationApp/doc.go" + ], + "find": + { + "height": 40.0 + }, + "find_in_files": + { + "height": 0.0, + "where_history": + [ + ] + }, + "find_state": + { + "case_sensitive": false, + "find_history": + [ + "newlineCount", + "nil", + ":", + "enum", + "ItemBreak", + "newlineCount", + "eob", + "a", + "f.eol = true", + "printnewline", + "f.eol = true", + "eol", + "case", + "return nil", + "return", + "parenDepth", + "formatParameters", + "peek", + "\"\\n\"))\n}", + "\"\\n\"))\n\t\t\t}", + "f.peek()", + "eol", + "ItemNewline", + "newlineCount", + "tabCount", + "if printSingleChar(f) {\n\t\t}", + "}", + "printTab", + "case", + "\\n" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + "" + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "selected": 0, + "sheets": + [ + { + "buffer": 0, + "file": "wsfmt.go", + "semi_transient": false, + "settings": + { + "buffer_size": 12155, + "regions": + { + }, + "selection": + [ + [ + 1793, + 1793 + ] + ], + "settings": + { + "syntax": "Packages/GoSublime/syntax/GoSublime-Go.sublime-syntax", + "translate_tabs_to_spaces": false + }, + "translation.x": 0.0, + "translation.y": 1256.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 25.0 + }, + "input": + { + "height": 38.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.9o:///home/timmy/GO/src/timmy.narnian.us/git/timmy/wsfmt": + { + "height": 115.0 + }, + "output.GoGuru Output": + { + "height": 0.0 + }, + "output.GoRename Output": + { + "height": 232.0 + }, + "output.GoSublime-main-output": + { + "height": 115.0 + }, + "output.GsComplete.completion-hint-output": + { + "height": 115.0 + }, + "output.GsDoc-output-output": + { + "height": 115.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "pinned_build_system": "Packages/GoSublime/GoSublime.sublime-build", + "project": "wsfmt.sublime-project", + "replace": + { + "height": 71.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 0.0 + }, + "select_project": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 0.0 + }, + "select_symbol": + { + "height": 392.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 392.0 + }, + "selected_group": 0, + "settings": + { + }, + "show_minimap": true, + "show_open_files": true, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 268.0, + "status_bar_visible": true, + "template_settings": + { + } +}