mirror of
https://github.com/sorenisanerd/gotty.git
synced 2024-11-09 23:34:26 +00:00
Simplify title format output procedure
This commit is contained in:
parent
46e33887c4
commit
f72b18052a
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -38,8 +38,8 @@
|
|||||||
"Rev": "5cf931ef8f76dccd0910001d74a58a7fca84a83d"
|
"Rev": "5cf931ef8f76dccd0910001d74a58a7fca84a83d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/yudai/utf8reader",
|
"ImportPath": "github.com/yudai/umutex",
|
||||||
"Rev": "0ccad3e5e2d8dc2493179319c4c8d1172f583ea4"
|
"Rev": "18216d265c6bc72c3bb0ad9c8103d47d530b7003"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
53
Godeps/_workspace/src/github.com/yudai/umutex/README.md
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/yudai/umutex/README.md
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Unblocking Mutex
|
||||||
|
|
||||||
|
This simple package provides unblocking mutexes for those who don't want to write many `select` clauses or get confused by numerous channels.
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/yudai/umutex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create mutex
|
||||||
|
mutex := umutex.New()
|
||||||
|
|
||||||
|
// First time, try should succeed
|
||||||
|
if mutex.TryLock() {
|
||||||
|
fmt.Println("SUCCESS")
|
||||||
|
} else {
|
||||||
|
fmt.Println("FAILURE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second time, try should fail as it's locked
|
||||||
|
if mutex.TryLock() {
|
||||||
|
fmt.Println("SUCCESS")
|
||||||
|
} else {
|
||||||
|
fmt.Println("FAILURE")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unclock mutex
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
// Third time, try should succeed again
|
||||||
|
if mutex.TryLock() {
|
||||||
|
fmt.Println("SUCCESS")
|
||||||
|
} else {
|
||||||
|
fmt.Println("FAILURE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The output is;
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SUCCESS
|
||||||
|
FAILURE
|
||||||
|
SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
`ForceLock()` method is also availale for normal blocking lock.
|
38
Godeps/_workspace/src/github.com/yudai/umutex/umutex.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/yudai/umutex/umutex.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Package umutex provides unblocking mutex
|
||||||
|
package umutex
|
||||||
|
|
||||||
|
// UnblockingMutex represents an unblocking mutex.
|
||||||
|
type UnblockingMutex struct {
|
||||||
|
// Raw channel
|
||||||
|
C chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returnes a new unblocking mutex instance.
|
||||||
|
func New() *UnblockingMutex {
|
||||||
|
return &UnblockingMutex{
|
||||||
|
C: make(chan bool, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock tries to lock the mutex.
|
||||||
|
// When the mutex is free at the time, the function locks the mutex and return
|
||||||
|
// true. Otherwise false will be returned. In the both cases, this function
|
||||||
|
// doens't block and return the result immediately.
|
||||||
|
func (m UnblockingMutex) TryLock() (result bool) {
|
||||||
|
select {
|
||||||
|
case m.C <- true:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unclocks the mutex.
|
||||||
|
func (m UnblockingMutex) Unlock() {
|
||||||
|
<-m.C
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceLock surely locks the mutex, however, this function blocks when the mutex is locked at the time.
|
||||||
|
func (m UnblockingMutex) ForceLock() {
|
||||||
|
m.C <- false
|
||||||
|
}
|
50
Godeps/_workspace/src/github.com/yudai/umutex/umutex_test.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/yudai/umutex/umutex_test.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package umutex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTryLock(t *testing.T) {
|
||||||
|
var result bool
|
||||||
|
|
||||||
|
mutex := New()
|
||||||
|
|
||||||
|
result = mutex.TryLock()
|
||||||
|
if result != true {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mutex.TryLock()
|
||||||
|
if result != false {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
result = mutex.TryLock()
|
||||||
|
if result != true {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForceLock(t *testing.T) {
|
||||||
|
var result bool
|
||||||
|
|
||||||
|
mutex := New()
|
||||||
|
|
||||||
|
result = mutex.TryLock()
|
||||||
|
if result != true {
|
||||||
|
t.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
mutex.ForceLock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
mutex.Unlock()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
69
Godeps/_workspace/src/github.com/yudai/utf8reader/README.md
generated
vendored
69
Godeps/_workspace/src/github.com/yudai/utf8reader/README.md
generated
vendored
@ -1,69 +0,0 @@
|
|||||||
# UTF8Reader for Go
|
|
||||||
|
|
||||||
UTF8Reader is a simple wrapper Reader that fills the given buffer with a "tail-safe" UTF8 byte sequence.
|
|
||||||
|
|
||||||
## Tail-Safe?
|
|
||||||
|
|
||||||
Let's say you have a buffer of 7 bytes and your Reader is going to fill your buffer with a UTF8 byte sequence.
|
|
||||||
|
|
||||||
```go
|
|
||||||
buf := make([]byte, 7)
|
|
||||||
reader := strings.NewReader("いろは")
|
|
||||||
|
|
||||||
reader.Read(buf)
|
|
||||||
```
|
|
||||||
|
|
||||||
The byte length of UTF8 characters is not fixed and some characters like the examples above have 3 byte length. There are others which have a single byte, 2 byte and 4 byte length as well. This means your buffer will be sometimes filled with incomplete bytes as an Unicode character at the tail.
|
|
||||||
|
|
||||||
By `reader.Read(buf)`, your `buf` will be like below:
|
|
||||||
|
|
||||||
```go
|
|
||||||
[]byte{
|
|
||||||
// い
|
|
||||||
byte(0xe3), // 1
|
|
||||||
byte(0x81), // 2
|
|
||||||
byte(0x84), // 3
|
|
||||||
// ろ
|
|
||||||
byte(0xe3), // 4
|
|
||||||
byte(0x82), // 5
|
|
||||||
byte(0x8d), // 6
|
|
||||||
// は (incomplete)
|
|
||||||
byte(0xe3), // 7
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The last character `は` is incomplete and the buffer is now invalid as a UTF8 string.
|
|
||||||
|
|
||||||
UTF8Reader detects incomplete bytes like above and aborts filling up the buffer in such cases.
|
|
||||||
|
|
||||||
```go
|
|
||||||
buf := make([]byte, 7)
|
|
||||||
reader := strings.NewReader("いろは")
|
|
||||||
utfReader := utf8reader.New(reader)
|
|
||||||
|
|
||||||
utfReader.Read(buf)
|
|
||||||
```
|
|
||||||
|
|
||||||
Then you will get:
|
|
||||||
|
|
||||||
```go
|
|
||||||
[]byte{
|
|
||||||
// い
|
|
||||||
byte(0xe3), // 1
|
|
||||||
byte(0x81), // 2
|
|
||||||
byte(0x84), // 3
|
|
||||||
// ろ
|
|
||||||
byte(0xe3), // 4
|
|
||||||
byte(0x82), // 5
|
|
||||||
byte(0x8d), // 6
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Of course, bytes left behind will be used to fill up the buffer on next `Read()`.
|
|
||||||
|
|
||||||
## Note
|
|
||||||
|
|
||||||
UTF8Reader just checks incomplete bytes at the tail of the buffer. Even if the original byte sequence given to UTF8Reader is broken, UTF8Reader reports no errors and just fills up the buffer.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License
|
|
56
Godeps/_workspace/src/github.com/yudai/utf8reader/utf8reader.go
generated
vendored
56
Godeps/_workspace/src/github.com/yudai/utf8reader/utf8reader.go
generated
vendored
@ -1,56 +0,0 @@
|
|||||||
package utf8reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SmallBufferError = errors.New("Buffer size must be larger than utf8.UTFMax.")
|
|
||||||
|
|
||||||
type UTF8Reader struct {
|
|
||||||
reader io.Reader
|
|
||||||
buffer *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(reader io.Reader) *UTF8Reader {
|
|
||||||
return &UTF8Reader{
|
|
||||||
reader: reader,
|
|
||||||
buffer: bytes.NewBuffer(make([]byte, 0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *UTF8Reader) Read(p []byte) (n int, err error) {
|
|
||||||
size := 0
|
|
||||||
|
|
||||||
if cap(p) < utf8.UTFMax {
|
|
||||||
return size, SmallBufferError
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.buffer.Len() > 0 {
|
|
||||||
n, err = r.buffer.Read(p)
|
|
||||||
size += n
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = r.reader.Read(p[size:])
|
|
||||||
size += n
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
|
|
||||||
leftOver := 0
|
|
||||||
for ; leftOver < utf8.UTFMax && size-leftOver > 0; leftOver++ {
|
|
||||||
rune, _ := utf8.DecodeLastRune(p[:size-leftOver])
|
|
||||||
if rune != utf8.RuneError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.buffer.Write(p[size-leftOver : size])
|
|
||||||
|
|
||||||
return size - leftOver, nil
|
|
||||||
}
|
|
97
Godeps/_workspace/src/github.com/yudai/utf8reader/utf8reader_test.go
generated
vendored
97
Godeps/_workspace/src/github.com/yudai/utf8reader/utf8reader_test.go
generated
vendored
@ -1,97 +0,0 @@
|
|||||||
package utf8reader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRead(t *testing.T) {
|
|
||||||
str := "日本語"
|
|
||||||
or := strings.NewReader(str)
|
|
||||||
r := New(or)
|
|
||||||
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
if bytes.Compare(buf[:n], []byte(str)) != 0 {
|
|
||||||
t.Errorf("Failed to read bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = r.Read(buf)
|
|
||||||
if err.Error() != "EOF" {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3 byte runes
|
|
||||||
str = "いろはにほ"
|
|
||||||
or = strings.NewReader(str)
|
|
||||||
r = New(or)
|
|
||||||
buf = make([]byte, 7) // 7 % 3 = 1
|
|
||||||
|
|
||||||
n, err = r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
if n != 6 {
|
|
||||||
t.Errorf("Read length error")
|
|
||||||
}
|
|
||||||
if bytes.Compare(buf[:n], []byte(str)[:6]) != 0 {
|
|
||||||
t.Errorf("Failed to read bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
if n != 6 {
|
|
||||||
t.Errorf("Read length error")
|
|
||||||
}
|
|
||||||
if bytes.Compare(buf[:n], []byte(str)[6:12]) != 0 {
|
|
||||||
t.Errorf("Failed to read bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
if n != 3 {
|
|
||||||
t.Errorf("Read length error")
|
|
||||||
}
|
|
||||||
if bytes.Compare(buf[:n], []byte(str)[12:15]) != 0 {
|
|
||||||
t.Errorf("Failed to read bytes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadWithSmallBuffer(t *testing.T) {
|
|
||||||
str := "日本語"
|
|
||||||
or := strings.NewReader(str)
|
|
||||||
r := New(or)
|
|
||||||
|
|
||||||
buf := make([]byte, 2) // too small
|
|
||||||
_, err := r.Read(buf)
|
|
||||||
if err != SmallBufferError {
|
|
||||||
t.Errorf("Expected error were not returned")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadWithSmallRead(t *testing.T) {
|
|
||||||
input := []byte("いろは")
|
|
||||||
or := bytes.NewBuffer(input[0:2]) // small read
|
|
||||||
r := New(or)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
|
|
||||||
_, err := r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
or.Write(input[2:6])
|
|
||||||
_, err = r.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
@ -118,19 +119,14 @@ func (context *clientContext) sendInitialize() error {
|
|||||||
RemoteAddr: context.request.RemoteAddr,
|
RemoteAddr: context.request.RemoteAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
context.writeMutex.Lock()
|
titleBuffer := new(bytes.Buffer)
|
||||||
writer, err := context.connection.NextWriter(websocket.TextMessage)
|
if err := context.app.titleTemplate.Execute(titleBuffer, titleVars); err != nil {
|
||||||
if err != nil {
|
|
||||||
context.writeMutex.Unlock()
|
context.writeMutex.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
writer.Write([]byte{SetWindowTitle})
|
if err := context.write(append([]byte{SetWindowTitle}, titleBuffer.Bytes()...)); err != nil {
|
||||||
if err = context.app.titleTemplate.Execute(writer, titleVars); err != nil {
|
|
||||||
context.writeMutex.Unlock()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
writer.Close()
|
|
||||||
context.writeMutex.Unlock()
|
|
||||||
|
|
||||||
htermPrefs := make(map[string]interface{})
|
htermPrefs := make(map[string]interface{})
|
||||||
for key, value := range context.app.options.Preferences {
|
for key, value := range context.app.options.Preferences {
|
||||||
|
Loading…
Reference in New Issue
Block a user