<commit_before>// Copyright © 2016, The T Authors.

package editor

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/eaburns/T/edit"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
)

// Server implements http.Handler, serving an HTTP text editor.
// It provides an HTTP API for creating buffers of text
// and editors to read and modify those buffers.
//
// Buffers and editors
//
// A buffer is an un-bounded sequence of runes.
// Buffers can be viewed and modified using editors.
// A buffer can have multiple editors,
// but each editor edits only a single buffer.
//
// An editor can view and modify a buffer
// using the T edit language documented here:
// https://godoc.org/github.com/eaburns/T/edit#Ed.
// While multiple editors can edit the same buffer concurrently,
// each editor maintains its own local state.
type Server struct {
	sync.RWMutex
	buffers map[string]*buffer
	editors map[string]*editor
	nextID  int
}

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{
		buffers: make(map[string]*buffer),
		editors: make(map[string]*editor),
	}
}

// Close closes the server and all of its buffers.
func (s *Server) Close() error {
	var errs []error
	for _, b := range s.buffers {
		errs = append(errs, b.close())
	}
	for _, err := range errs {
		if err != nil {
			return err
		}
	}
	return nil
}

// RegisterHandlers registers handlers for the following paths and methods:
//  /buffers is the list of opened buffers.
//
// 	GET returns a Buffer list of the opened buffers.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
//
// 	PUT creates a new, empty buffer and returns its Buffer.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
//
//  /buffer/<ID> is the buffer with the given ID.
//
// 	GET returns the buffer's Buffer
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
// 	DELETE deletes the buffer and all of its editors.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
// 	PUT creates a new editor for the buffer and returns its Editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
//  /buffer/<ID>/changes is the buffer's change stream.
//
// 	GET upgrades the connection to a websocket.
// 	A ChangeList is sent on the websocket
// 	for each edit made to the buffer.
// 	Returns:
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
//  /editor/<ID> is the editor with the given ID.
//
// 	GET returns the editor's Editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
//
// 	DELETE deletes the editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor editor is not found.
//
//  /editor/<ID>/text is the text that the editor edits.
//
// 	GET returns the text of the editor's buffer.
// 	Parameters:
// 	• addr can optionally be set to an address string.
// 	  It must not appear multiple times, there can only be one addr.
// 	  If it is set, only the text within the address is returned.
//  	  Otherwise, all text is returned.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
// 	• Bad Request if the URL parameters or addr value are malformed.
// 	• Range Not Satisfiable if there is an error evaluating the address.
// 	  The response body will contain an error message.
//
// 	POST performs an atomic sequence of edits on the buffer.
// 	The body must be an ordered list of Edits.
// 	The response is an ordered list of EditResult.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
// 	• Bad Request if the Edit list is malformed.
//
// Unless otherwise stated, the body of all error responses is the error message.
func (s *Server) RegisterHandlers(r *mux.Router) {
	r.HandleFunc("/buffers", s.listBuffers).Methods(http.MethodGet)
	r.HandleFunc("/buffers", s.newBuffer).Methods(http.MethodPut)
	r.HandleFunc("/buffer/{id}", s.bufferInfo).Methods(http.MethodGet)
	r.HandleFunc("/buffer/{id}", s.closeBuffer).Methods(http.MethodDelete)
	r.HandleFunc("/buffer/{id}", s.newEditor).Methods(http.MethodPut)
	r.HandleFunc("/buffer/{id}/changes", s.changes).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}", s.editorInfo).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}", s.closeEditor).Methods(http.MethodDelete)
	r.HandleFunc("/editor/{id}/text", s.read).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}/text", s.edit).Methods(http.MethodPost)
}

// respond JSON encodes resp to w, and sends an Internal Server Error on failure.
func respond(w http.ResponseWriter, resp interface{}) {
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) listBuffers(w http.ResponseWriter, req *http.Request) {
	s.RLock()
	var bufs []Buffer
	for _, b := range s.buffers {
		bufs = append(bufs, b.Buffer)
	}
	s.RUnlock()

	respond(w, bufs)
}

func (s *Server) newBuffer(w http.ResponseWriter, req *http.Request) {
	s.Lock()
	id := strconv.Itoa(s.nextID)
	s.nextID++
	buf := &buffer{
		Buffer: Buffer{
			ID:   id,
			Path: path.Join("/", "buffer", id),
		},<commit_msg>editor: set Content-Type and Content-Length in response headers.<commit_after>// Copyright © 2016, The T Authors.

package editor

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/eaburns/T/edit"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
)

// Server implements http.Handler, serving an HTTP text editor.
// It provides an HTTP API for creating buffers of text
// and editors to read and modify those buffers.
//
// Buffers and editors
//
// A buffer is an un-bounded sequence of runes.
// Buffers can be viewed and modified using editors.
// A buffer can have multiple editors,
// but each editor edits only a single buffer.
//
// An editor can view and modify a buffer
// using the T edit language documented here:
// https://godoc.org/github.com/eaburns/T/edit#Ed.
// While multiple editors can edit the same buffer concurrently,
// each editor maintains its own local state.
type Server struct {
	sync.RWMutex
	buffers map[string]*buffer
	editors map[string]*editor
	nextID  int
}

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{
		buffers: make(map[string]*buffer),
		editors: make(map[string]*editor),
	}
}

// Close closes the server and all of its buffers.
func (s *Server) Close() error {
	var errs []error
	for _, b := range s.buffers {
		errs = append(errs, b.close())
	}
	for _, err := range errs {
		if err != nil {
			return err
		}
	}
	return nil
}

// RegisterHandlers registers handlers for the following paths and methods:
//  /buffers is the list of opened buffers.
//
// 	GET returns a Buffer list of the opened buffers.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
//
// 	PUT creates a new, empty buffer and returns its Buffer.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
//
//  /buffer/<ID> is the buffer with the given ID.
//
// 	GET returns the buffer's Buffer
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
// 	DELETE deletes the buffer and all of its editors.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
// 	PUT creates a new editor for the buffer and returns its Editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
//  /buffer/<ID>/changes is the buffer's change stream.
//
// 	GET upgrades the connection to a websocket.
// 	A ChangeList is sent on the websocket
// 	for each edit made to the buffer.
// 	Returns:
// 	• Internal Server Error on internal error.
// 	• Not Found if the buffer is not found.
//
//  /editor/<ID> is the editor with the given ID.
//
// 	GET returns the editor's Editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
//
// 	DELETE deletes the editor.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor editor is not found.
//
//  /editor/<ID>/text is the text that the editor edits.
//
// 	GET returns the text of the editor's buffer.
// 	Parameters:
// 	• addr can optionally be set to an address string.
// 	  It must not appear multiple times, there can only be one addr.
// 	  If it is set, only the text within the address is returned.
//  	  Otherwise, all text is returned.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
// 	• Bad Request if the URL parameters or addr value are malformed.
// 	• Range Not Satisfiable if there is an error evaluating the address.
// 	  The response body will contain an error message.
//
// 	POST performs an atomic sequence of edits on the buffer.
// 	The body must be an ordered list of Edits.
// 	The response is an ordered list of EditResult.
// 	Returns:
// 	• OK on success.
// 	• Internal Server Error on internal error.
// 	• Not Found if the editor is not found.
// 	• Bad Request if the Edit list is malformed.
//
// Unless otherwise stated, the body of all error responses is the error message.
func (s *Server) RegisterHandlers(r *mux.Router) {
	r.HandleFunc("/buffers", s.listBuffers).Methods(http.MethodGet)
	r.HandleFunc("/buffers", s.newBuffer).Methods(http.MethodPut)
	r.HandleFunc("/buffer/{id}", s.bufferInfo).Methods(http.MethodGet)
	r.HandleFunc("/buffer/{id}", s.closeBuffer).Methods(http.MethodDelete)
	r.HandleFunc("/buffer/{id}", s.newEditor).Methods(http.MethodPut)
	r.HandleFunc("/buffer/{id}/changes", s.changes).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}", s.editorInfo).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}", s.closeEditor).Methods(http.MethodDelete)
	r.HandleFunc("/editor/{id}/text", s.read).Methods(http.MethodGet)
	r.HandleFunc("/editor/{id}/text", s.edit).Methods(http.MethodPost)
}

// respond JSON encodes resp to w, and sends an Internal Server Error on failure.
func respond(w http.ResponseWriter, resp interface{}) {
	body, err := json.Marshal(resp)
	if err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Content-Length", strconv.Itoa(len(body)))
		_, err = w.Write(body)
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (s *Server) listBuffers(w http.ResponseWriter, req *http.Request) {
	s.RLock()
	var bufs []Buffer
	for _, b := range s.buffers {
		bufs = append(bufs, b.Buffer)
	}
	s.RUnlock()

	respond(w, bufs)
}

func (s *Server) newBuffer(w http.ResponseWriter, req *http.Request) {
	s.Lock()
	id := strconv.Itoa(s.nextID)
	s.nextID++
	buf := &buffer{
		Buffer: Buffer{
			ID:   id,
			Path: path.Join("/", "buffer", id),
		},
{'fraction_non_alphanumeric': 0.1304044630404463, 'fraction_numerical': 0.000697350069735007, 'mean_word_length': 3.0440606274233346, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6564247', 'n_tokens_mistral': 3818, 'n_tokens_neox': 3724, 'n_words': 1431}