index page

This commit is contained in:
Felix Angell 2018-04-29 19:18:13 +01:00
parent 5fccad1a58
commit 5eeb782e3d
33 changed files with 6 additions and 2983 deletions

View File

@ -1,8 +0,0 @@
# Contributing
This is a little guide to help you contribute to the editor. Check the README for
build/install instructions.
## Creating Issues
Reporting bugs alone is a great help. If you catch anything please report it
into the issue tracker. It's appreciated if you check that you are not reporting
a bug that has been reported before.

12
DOCS.md
View File

@ -1,12 +0,0 @@
# Documentation
This file is documentation for the editor itself. It covers:
* a list of commands availible;
* configuration options; and
* adding new syntax configurations to the editor;
Contributions to this are welcome. Note: due to the volatility
of the editor, this documentation will be outdated, have mistakes,
spelling errors, and more.
## Index

View File

@ -1,7 +0,0 @@
Copyright (c) 2016 Felix Angell
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.

145
README.md
View File

@ -1,145 +0,0 @@
<p align="center"><img src="res/icons/icon96.png"></p>
<h1>phi</h1>
Phi is a minimal code editor designed to look pretty, run fast, and be easy
to configure and use. It's primary function is for editing code.
Note that this is a work in progress and is very buggy! The editor is written as if it's a game,
so it will probably **eat up your battery**, as well as **run possibly quite slow** - especially
if you dont have a dedicated GPU - and probably **crash frequently**.
**Do not edit your precious files with this editor!**
<br>
Here's a screenshot of Phi in action:
<p align="center"><img src="screenshot.png"></p>
# goals
The editor must:
* run fast;
* load and edit large files with ease;
* look pretty; and finally
* be easy to use
## non-goals
The editor probably wont:
* have any plugin support;
* be very customizable in terms of layout;
* support many non utf8 encodings;
* support non true-type-fonts;
* support right-to-left languages;
Perhaps in the future if I have time to get round to some of these
I may. Avoiding most of these is to avoid complexity in the code-base
and general architecture of the editor.
# why?
The editor does not exist as a serious replacement to Sublime Text/Atom/Emacs/[editor name here].
Though one of my big goals for the project is to possibly replace sublime text for my own personal use. Thus the editor is somewhat optimized for my own work-flow.
The code is up purely for people to look at and maybe use or contribute or whatever. Sharing is caring!
# reporting bugs/troubleshooting
Note the editor is still unstable. Please report any bugs you find so I can
squash them! It is appreciated if you skim the issue handler to make sure
you aren't reporting duplicate bugs.
## before filing an issue
Just to make sure it's an issue with the editor currently and not due to a
broken change, please:
* make sure the repository is up to date
* make sure all the dependencies are updated, especially "github.com/felixangell/strife"
* try removing the ~/.phi-config folder manually and letting the editor re-load it
# building
## requirements
You will need the go compiler installed with the GOPATH/GOBIN/etc setup. In addition
you will need the following libraries:
* sdl2
* sdl2_image
* sdl2_ttf
If you're on Linux, you will need:
* xsel
* xclip
Either works. This is for copying/pasting.
### linux
Here's an example for Ubuntu:
```bash
$ sudo apt-get install libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev xclip
$ go get github.com/felixangell/phi-editor
$ cd $GOPATH/src/github.com/felixangell/phi-editor
$ go build
$ ./phi-editor
```
### macOS
If you're on macOS, you can do something like this, using homebrew:
```bash
$ brew install sdl2 sdl2_image sdl2_ttf pkg-config
$ go get github.com/felixangell/phi-editor
$ cd $GOPATH/src/github.com/felixangell/phi-editor
$ go build
$ ./phi-editor
```
### windows
If you're on windows, you have my condolences.
## configuration
Configuration files are stored in `$HOME/.phi-editor/config.toml`. Note that
this directory is created on first startup by the editor, as well as the configuration
files in the 'cfg/' directory are pre-loaded dependening on platform: see 'cfg/linuxconfig.go', for example.
Below is an incomplete configuration file to give you an
idea of what the config files are like:
```toml
[editor]
tab_size = 4
hungry_backspace = true
tabs_are_spaces = true
match_braces = false
maintain_indentation = true
highlight_line = true
[render]
aliased = true
accelerated = true
throttle_cpu_usage = true
always_render = true
[theme]
background = 0x002649
foreground = 0xf2f4f6
cursor = 0xf2f4f6
cursor_invert = 0x000000
[cursor]
flash_rate = 400
reset_delay = 400
draw = true
flash = true
[commands]
[commands.save]
shortcut = "super+s"
[commands.delete_line]
shortcut = "super+d"
```
# license
[MIT License](/LICENSE)

View File

@ -1,134 +0,0 @@
package cfg
import (
"errors"
"github.com/felixangell/strife"
"log"
"regexp"
"strconv"
)
type TomlConfig struct {
Editor EditorConfig `toml:"editor"`
Cursor CursorConfig `toml:"cursor"`
Render RenderConfig `toml:"render"`
Theme ThemeConfig `toml:"theme"`
Associations map[string]FileAssociations `toml:"file_associations"`
Commands map[string]Command `toml:"commands"`
associations map[string]*LanguageSyntaxConfig
}
// GetSyntaxConfig returns a pointer to the parsed
// syntax language file for the given file extension
// e.g. what syntax def we need for a .cpp file or a .h file
func (t *TomlConfig) GetSyntaxConfig(ext string) (*LanguageSyntaxConfig, error) {
if val, ok := t.associations[ext]; ok {
return val, nil
}
return nil, errors.New("no language for extension '" + ext + "'")
}
type FileAssociations struct {
Extensions []string
}
type SyntaxCriteria struct {
Colour int `toml:"colouring"`
Match []string `toml:"match"`
Pattern string `toml:"pattern"`
CompiledPattern *regexp.Regexp
MatchList map[string]bool
}
type Command struct {
Shortcut string
}
type CursorConfig struct {
Flash_Rate int64
Reset_Delay int64
Draw bool
Flash bool
Block_Width string
}
func (c CursorConfig) GetCaretWidth() int {
if c.Block_Width == "block" {
return -1
}
if c.Block_Width == "" {
return -1
}
value, err := strconv.ParseInt(c.Block_Width, 10, 32)
if err != nil {
panic(err)
}
return int(value)
}
type RenderConfig struct {
Aliased bool
Accelerated bool
Throttle_Cpu_Usage bool
Always_Render bool
Syntax_Highlighting bool
}
// todo make this more extendable...
// e.g. .phi-editor/themes with TOML
// themes in them and we can select
// the default theme in the EditorConfig
// instead.
type ThemeConfig struct {
Background int32
Foreground int32
Cursor int32
Cursor_Invert int32
Palette PaletteConfig
Gutter_Background int32
Gutter_Foreground int32
}
type PaletteConfig struct {
Background int32
Foreground int32
Cursor int32
Outline int32
Render_Shadow bool
Shadow_Color int32
Suggestion struct {
Background int32
Foreground int32
Selected_Background int32
Selected_Foreground int32
}
}
type EditorConfig struct {
Tab_Size int
Hungry_Backspace bool
Tabs_Are_Spaces bool
Match_Braces bool
Maintain_Indentation bool
Highlight_Line bool
Font_Face string
Font_Size int
Show_Line_Numbers bool
Loaded_Font *strife.Font
}
func NewDefaultConfig() *TomlConfig {
log.Println("Loading default configuration... this should never happen")
return &TomlConfig{
Editor: EditorConfig{},
Theme: ThemeConfig{
Background: 0x002649,
Foreground: 0xf2f4f6,
Cursor: 0xf2f4f6,
Cursor_Invert: 0xffffff,
},
}
}

View File

@ -1,82 +0,0 @@
// +build !linux,!darwin
package cfg
var DEFUALT_TOML_CONFIG = `[editor]
tab_size = 4
hungry_backspace = true
tabs_are_spaces = true
match_braces = false
maintain_indentation = true
highlight_line = true
font_face = "Courier New"
font_size = 20
show_line_numbers = true
[render]
aliased = true
accelerated = true
throttle_cpu_usage = true
always_render = true
syntax_highlighting = true
[file_associations]
[file_associations.toml]
extensions = [".toml"]
[file_associations.c]
extensions = [".c", ".h", ".cc"]
[file_associations.go]
extensions = [".go"]
[file_associations.md]
extensions = [".md"]
[theme]
background = 0x002649
foreground = 0xf2f4f6
cursor = 0xf2f4f6
cursor_invert = 0x000000
gutter_background = 0x000000
gutter_foreground = 0xf2f4f6
[theme.palette]
outline = 0xebedef
background = 0xffffff
foreground = 0x000000
cursor = 0xf2f4f6
render_shadow = true
shadow_color = 0x000000
[theme.palette.suggestion]
background = 0xebedef
foreground = 0x3a3839
selected_background = 0xc7cbd1
selected_foreground = 0x3a3839
[cursor]
flash_rate = 400
reset_delay = 400
draw = true
flash = true
[commands]
[commands.save]
shortcut = "ctrl+s"
[commands.show_palette]
shortcut = "ctrl+p"
[commands.paste]
shortcut = "ctrl+v"
[commands.exit]
shortcut = "ctrl+q"
[commands.close_buffer]
shortcut = "ctrl+w"
[commands.delete_line]
shortcut = "ctrl+d"
`

View File

@ -1,82 +0,0 @@
// +build linux
package cfg
var DEFUALT_TOML_CONFIG = `[editor]
tab_size = 4
hungry_backspace = true
tabs_are_spaces = true
match_braces = false
maintain_indentation = true
highlight_line = true
font_face = "Courier"
font_size = 20
show_line_numbers = true
[render]
aliased = true
accelerated = true
throttle_cpu_usage = true
always_render = true
syntax_highlighting = true
[file_associations]
[file_associations.toml]
extensions = [".toml"]
[file_associations.c]
extensions = [".c", ".h", ".cc"]
[file_associations.go]
extensions = [".go"]
[file_associations.md]
extensions = [".md"]
[theme]
background = 0x002649
foreground = 0xf2f4f6
cursor = 0xf2f4f6
cursor_invert = 0x000000
gutter_background = 0x000000
gutter_foreground = 0xf2f4f6
[theme.palette]
outline = 0xebedef
background = 0xffffff
foreground = 0x000000
cursor = 0xf2f4f6
render_shadow = true
shadow_color = 0x000000
[theme.palette.suggestion]
background = 0xebedef
foreground = 0x3a3839
selected_background = 0xc7cbd1
selected_foreground = 0x3a3839
[cursor]
flash_rate = 400
reset_delay = 400
draw = true
flash = true
[commands]
[commands.exit]
shortcut = "ctrl+q"
[commands.show_palette]
shortcut = "ctrl+p"
[commands.save]
shortcut = "ctrl+s"
[commands.paste]
shortcut = "ctrl+v"
[commands.close_buffer]
shortcut = "ctrl+w"
[commands.delete_line]
shortcut = "ctrl+d"
`

View File

@ -1,82 +0,0 @@
// +build darwin
package cfg
var DEFUALT_TOML_CONFIG = `[editor]
tab_size = 4
hungry_backspace = true
tabs_are_spaces = true
match_braces = false
maintain_indentation = true
highlight_line = true
font_face = "Courier New"
font_size = 20
show_line_numbers = true
[render]
aliased = true
accelerated = true
throttle_cpu_usage = true
always_render = true
syntax_highlighting = true
[file_associations]
[file_associations.toml]
extensions = [".toml"]
[file_associations.c]
extensions = [".c", ".h", ".cc"]
[file_associations.go]
extensions = [".go"]
[file_associations.md]
extensions = [".md"]
[theme]
background = 0x002649
foreground = 0xf2f4f6
cursor = 0xf2f4f6
cursor_invert = 0x000000
gutter_background = 0x000000
gutter_foreground = 0xf2f4f6
[theme.palette]
outline = 0xebedef
background = 0xffffff
foreground = 0x000000
cursor = 0xf2f4f6
render_shadow = true
shadow_color = 0x000000
[theme.palette.suggestion]
background = 0xebedef
foreground = 0x3a3839
selected_background = 0xc7cbd1
selected_foreground = 0x3a3839
[cursor]
flash_rate = 400
reset_delay = 400
draw = true
flash = true
[commands]
[commands.exit]
shortcut = "super+q"
[commands.save]
shortcut = "super+s"
[commands.show_palette]
shortcut = "super+p"
[commands.paste]
shortcut = "super+v"
[commands.close_buffer]
shortcut = "super+w"
[commands.delete_line]
shortcut = "super+d"
`

View File

@ -1,258 +0,0 @@
package cfg
import (
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/felixangell/strife"
// fork of BurntSushi with hexadecimal support.
"github.com/felixangell/toml"
)
// TODO:
// - make the $HOME/.phi-editor folder if it doesn't exist
// - make the $HOME/.phi-editor/config.toml file if it doesn't exist
// - write a default toml file
//
const (
CONFIG_DIR_PATH = "/.phi-editor/"
CONFIG_TOML_FILE = "config.toml"
)
// this is the absolute path to the
// config.toml file. todo rename/refactor
var CONFIG_FULL_PATH string = ""
// the absolute path to the config directory
// rename/refactor due here too!
var configDirAbsPath string = ""
// TODO we only had double key combos
// e.g. cmd+s. we want to handle things
// like cmd+alt+s
type shortcutRegister struct {
Supers map[string]string
Controls map[string]string
}
var Shortcuts = &shortcutRegister{
Supers: map[string]string{},
Controls: map[string]string{},
}
func loadSyntaxDef(lang string) *LanguageSyntaxConfig {
languagePath := filepath.Join(configDirAbsPath, "syntax", lang+".toml")
log.Println("Loading lang from ", languagePath)
syntaxTomlData, err := ioutil.ReadFile(languagePath)
if err != nil {
log.Println("Failed to load highlighting for language '"+lang+"' from path: ", languagePath)
return nil
}
var conf = &LanguageSyntaxConfig{}
if _, err := toml.Decode(string(syntaxTomlData), conf); err != nil {
panic(err)
}
log.Println("Loaded syntax definition for language", lang)
return conf
}
func findFontFolder() string {
// TODO
return "/usr/share/fonts/"
}
func configureAndValidate(conf *TomlConfig) {
// fonts
log.Println("Configuring fonts")
{
var fontFolder string
switch runtime.GOOS {
case "windows":
fontFolder = filepath.Join(os.Getenv("WINDIR"), "fonts")
case "darwin":
fontFolder = "/Library/Fonts/"
case "linux":
fontFolder = findFontFolder()
}
// we only support ttf at the moment.
fontPath := filepath.Join(fontFolder, conf.Editor.Font_Face) + ".ttf"
if _, err := os.Stat(fontPath); os.IsNotExist(err) {
log.Fatal("No such font '" + fontPath + "'")
// TODO cool error messages for the toml format?
os.Exit(0)
}
// load the font!
font, err := strife.LoadFont(fontPath, conf.Editor.Font_Size)
if err != nil {
panic(err)
}
conf.Editor.Loaded_Font = font
}
// config & validate the keyboard shortcuts
log.Println("Configuring keyboard shortcuts")
{
// keyboard commands
for commandName, cmd := range conf.Commands {
shortcut := cmd.Shortcut
vals := strings.Split(shortcut, "+")
// TODO handle conflicts
switch vals[0] {
case "super":
Shortcuts.Supers[vals[1]] = commandName
case "ctrl":
Shortcuts.Controls[vals[1]] = commandName
}
}
}
log.Println("Syntax Highlighting")
{
syntaxSet := []*LanguageSyntaxConfig{}
conf.associations = map[string]*LanguageSyntaxConfig{}
for lang, extSet := range conf.Associations {
log.Println(lang, "=>", extSet.Extensions)
languageConfig := loadSyntaxDef(lang)
// check for errors here
syntaxSet = append(syntaxSet, languageConfig)
for _, ext := range extSet.Extensions {
log.Println("registering", ext, "as", lang)
conf.associations[ext] = languageConfig
}
}
// go through each language
// and store the matches keywords
// as a hashmap for faster lookup
// in addition to this we compile any
// regular expressions if necessary.
for _, language := range syntaxSet {
for _, syn := range language.Syntax {
syn.MatchList = map[string]bool{}
if syn.Pattern != "" {
regex, err := regexp.Compile(syn.Pattern)
if err != nil {
log.Println(err.Error())
continue
}
syn.CompiledPattern = regex
} else {
for _, item := range syn.Match {
if _, ok := syn.MatchList[item]; ok {
log.Println("Warning duplicate match item '" + item + "'")
continue
}
syn.MatchList[item] = true
}
}
}
}
}
}
func Setup() TomlConfig {
log.Println("Setting up Phi Editor")
home := os.Getenv("HOME")
if runtime.GOOS == "windows" {
home = os.Getenv("USERPROFILE")
}
CONFIG_DIR := filepath.Join(home, CONFIG_DIR_PATH)
configDirAbsPath = CONFIG_DIR
CONFIG_PATH := filepath.Join(CONFIG_DIR, CONFIG_TOML_FILE)
// this folder is where we store all of the language syntax
SYNTAX_CONFIG_DIR := filepath.Join(CONFIG_DIR, "syntax")
CONFIG_FULL_PATH = CONFIG_PATH
// if the user doesn't have a /.phi-editor
// directory we create it for them.
if _, err := os.Stat(CONFIG_DIR); os.IsNotExist(err) {
if err := os.Mkdir(CONFIG_DIR, 0775); err != nil {
panic(err)
}
}
// try make the syntax config folder.
if _, err := os.Stat(SYNTAX_CONFIG_DIR); os.IsNotExist(err) {
if err := os.Mkdir(SYNTAX_CONFIG_DIR, 0775); err != nil {
panic(err)
}
// load all of the default language syntax
for name, syntaxDef := range DefaultSyntaxSet {
languagePath := filepath.Join(SYNTAX_CONFIG_DIR, name+".toml")
if _, err := os.Stat(languagePath); os.IsNotExist(err) {
file, err := os.Create(languagePath)
if err != nil {
panic(err)
}
defer file.Close()
if _, err := file.Write([]byte(syntaxDef)); err != nil {
panic(err)
}
log.Println("Wrote syntax for language '" + name + "'")
}
}
}
// make sure a config.toml file exists in the
// phi-editor directory.
if _, err := os.Stat(CONFIG_PATH); os.IsNotExist(err) {
configFile, fileCreateErr := os.Create(CONFIG_PATH)
if fileCreateErr != nil {
panic(fileCreateErr)
}
defer configFile.Close()
// write some stuff
_, writeErr := configFile.Write([]byte(DEFUALT_TOML_CONFIG))
if writeErr != nil {
panic(writeErr)
}
configFile.Sync()
}
if _, err := os.Open(CONFIG_PATH); err != nil {
panic(err)
}
configTomlData, err := ioutil.ReadFile(CONFIG_PATH)
if err != nil {
panic(err)
}
var conf TomlConfig
if _, err := toml.Decode(string(configTomlData), &conf); err != nil {
panic(err)
}
configureAndValidate(&conf)
return conf
}

View File

@ -1,120 +0,0 @@
package cfg
type LanguageSyntaxConfig struct {
Syntax map[string]*SyntaxCriteria `toml:"syntax"`
}
type DefaultSyntax map[string]string
var DefaultSyntaxSet = DefaultSyntax{}
func RegisterSyntax(name string, s string) {
DefaultSyntaxSet[name] = s
}
func init() {
// TOML
RegisterSyntax("toml", `[syntax.toml]
[syntax.declaration]
colouring = 0xf8f273
pattern = '(\[)(.*)(\])'
[syntax.identifier]
colouring = 0xf0a400
pattern = '\b([a-z]|[A-Z])+(_|([a-z]|[A-Z])+)*\b'
[syntax.symbol]
match = ["="]
colouring = 0xf8f273
`)
// C LANGUAGE SYNTAX HIGHLIGHTING
RegisterSyntax("c", `[syntax.c]
[syntax.type]
colouring = 0xf8f273
match = [
"int", "char", "bool", "float", "double", "void",
"uint8_t", "uint16_t", "uint32_t", "uint64_t",
"int8_t", "int16_t", "int32_t", "int64_t", "const"
]
[syntax.keyword]
colouring = 0xf0a400
match = [
"for", "break", "if", "else", "continue", "return",
"goto", "extern", "const", "typedef",
"struct", "union", "register", "enum",
"do", "static", "sizeof", "volatile", "unsigned",
"switch", "case", "default"
]
[syntax.string_literal]
colouring = 0x4b79fc
pattern = "\"([^\\\"]|\\.)*\""
[syntax.directive]
colouring = 0xf0a400
pattern = "^\\s*#\\s*include\\s+(?:<[^>]*>|\"[^\"]*\")\\s*"
[syntax.symbol]
colouring = 0xf0a400
match = [
"+=", "-=", "*=", "/=", ">>", "<<", "==", "!=",
">=", "<=", "||", "&&",
"=", ":", ";", "*", "&", "+", "-", "/", "%",
"^", "#", "!", "@", "<", ">", ".", ","
]
[syntax.comment]
colouring = 0x4b79fc
pattern = '//.*'`)
// GO LANGUAGE SYNTAX HIGHLIGHTING
RegisterSyntax("go", `[syntax.go]
[syntax.keyword]
colouring = 0xf0a400
match = [
"break", "default", "func", "interface", "select",
"case", "defer", "go", "map", "struct",
"chan", "else", "goto", "package", "switch",
"const", "fallthrough", "if", "range", "type",
"continue", "for", "import", "return", "var",
]
[syntax.type]
colouring = 0xf8f273
match = [
"int", "string", "uint", "rune",
"int8", "int16", "int32", "int64",
"uint8", "uint16", "uint32", "uint64",
"byte", "float32", "float64", "complex64",
"complex128", "uintptr",
]
[syntax.comment]
colouring = 0x4b79fc
pattern = '//.*'
[syntax.string_literal]
colouring = 0x4b79fc
pattern = "\"([^\\\"]|\\.)*\""
[syntax.symbol]
colouring = 0xf0a400
match = [
"+=", "-=", "*=", "/=", ">>", "<<", "==", "!=", ":=",
">=", "<=", "||", "&&",
"=", ":", ";", "*", "&", "+", "-", "/", "%",
"^", "#", "!", "@", "<", ">", ".", ","
]`)
RegisterSyntax("md", `[syntax.md]
[syntax.header]
colouring = 0xff00ff
pattern = '(?m)^#{1,6}.*'
`)
// your syntax here!
}

View File

@ -1,42 +0,0 @@
package gui
import "os"
type BufferAction struct {
name string
proc func(*View, []string) bool
}
func NewBufferAction(name string, proc func(*View, []string) bool) BufferAction {
return BufferAction{
name: name,
proc: proc,
}
}
func NewFile(v *View, commands []string) bool {
// TODO some nice error stuff
// have an error roll thing in the view?
buff := v.AddBuffer()
buff.OpenFile(commands[0])
buff.SetFocus(true)
v.focusedBuff = buff.index
return false
}
var actions = map[string]BufferAction{
"new": NewBufferAction("new", NewFile),
"save": NewBufferAction("save", Save),
"delete_line": NewBufferAction("delete_line", DeleteLine),
"close_buffer": NewBufferAction("close_buffer", CloseBuffer),
"paste": NewBufferAction("paste", Paste),
"show_palette": NewBufferAction("show_palette", ShowPalette),
"exit": NewBufferAction("exit", func(*View, []string) bool {
// TODO do this properly lol
os.Exit(0)
return false
}),
}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
package gui
func CloseBuffer(v *View, commands []string) bool {
b := v.getCurrentBuff()
if b == nil {
return false
}
if len(v.buffers) > 1 {
v.ChangeFocus(-1)
}
v.removeBuffer(b.index)
return false
}

View File

@ -1,58 +0,0 @@
package gui
import (
"github.com/felixangell/strife"
)
type Component interface {
SetPosition(x, y int)
Translate(x, y int)
Resize(w, h int)
OnInit()
OnUpdate() bool
OnRender(*strife.Renderer)
OnDispose()
NumComponents() int
AddComponent(c Component)
GetComponents() []Component
HandleEvent(evt strife.StrifeEvent)
SetFocus(focus bool)
HasFocus() bool
}
type BaseComponent struct {
x, y int
w, h int
focused bool
}
func (b *BaseComponent) SetFocus(focus bool) {
b.focused = focus
}
func (b *BaseComponent) HasFocus() bool {
return b.focused
}
func (b *BaseComponent) HandleEvent(evt strife.StrifeEvent) {
// NOP
}
func (b *BaseComponent) SetPosition(x, y int) {
b.x = x
b.y = y
}
func (b *BaseComponent) Translate(x, y int) {
b.x += x
b.y += y
}
func (b *BaseComponent) Resize(w, h int) {
b.w = w
b.h = h
}

View File

@ -1,26 +0,0 @@
package gui
type Cursor struct {
x, y int
rx, ry int
}
func (c *Cursor) gotoStart() {
for c.x > 1 {
c.move(-1, 0)
}
}
func (c *Cursor) move(x, y int) {
c.moveRender(x, y, x, y)
}
// moves the cursors position, and the
// rendered coordinates by the given amount
func (c *Cursor) moveRender(x, y, rx, ry int) {
c.x += x
c.y += y
c.rx += rx
c.ry += ry
}

View File

@ -1,11 +0,0 @@
package gui
func DeleteLine(v *View, commands []string) bool {
b := v.getCurrentBuff()
if b == nil {
return false
}
b.deleteLine()
return true
}

View File

@ -1,258 +0,0 @@
package gui
import (
"github.com/felixangell/fuzzysearch/fuzzy"
"github.com/felixangell/phi/cfg"
"github.com/felixangell/strife"
"github.com/veandco/go-sdl2/sdl"
"log"
"strings"
)
var commandSet []string
func init() {
commandSet = make([]string, len(actions))
for _, action := range actions {
commandSet = append(commandSet, action.name)
}
}
type CommandPalette struct {
BaseComponent
buff *Buffer
parentBuff *Buffer
conf *cfg.TomlConfig
parent *View
suggestionIndex int
recentSuggestions *[]suggestion
}
var suggestionBoxHeight, suggestionBoxWidth = 48, 0
type suggestion struct {
parent *CommandPalette
name string
}
func (s *suggestion) renderHighlighted(x, y int, ctx *strife.Renderer) {
// wewlad
conf := s.parent.conf.Theme.Palette
border := 5
ctx.SetColor(strife.HexRGB(conf.Outline))
ctx.Rect(x-border, y-border, suggestionBoxWidth+(border*2), suggestionBoxHeight+(border*2), strife.Fill)
ctx.SetColor(strife.HexRGB(conf.Suggestion.Selected_Background))
ctx.Rect(x, y, suggestionBoxWidth, suggestionBoxHeight, strife.Fill)
ctx.SetColor(strife.HexRGB(conf.Suggestion.Selected_Foreground))
// FIXME strife library needs something to get
// text width and heights... for now we render offscreen to measure... lol
_, h := ctx.String("foo", -500000, -50000)
yOffs := (suggestionBoxHeight / 2) - (h / 2)
ctx.String(s.name, x+border, y+yOffs)
}
func (s *suggestion) render(x, y int, ctx *strife.Renderer) {
// wewlad
conf := s.parent.conf.Theme.Palette
border := 5
ctx.SetColor(strife.HexRGB(conf.Outline))
ctx.Rect(x-border, y-border, suggestionBoxWidth+(border*2), suggestionBoxHeight+(border*2), strife.Fill)
ctx.SetColor(strife.HexRGB(conf.Suggestion.Background))
ctx.Rect(x, y, suggestionBoxWidth, suggestionBoxHeight, strife.Fill)
ctx.SetColor(strife.HexRGB(conf.Suggestion.Foreground))
// FIXME strife library needs something to get
// text width and heights... for now we render offscreen to measure... lol
_, h := ctx.String("foo", -500000, -50000)
yOffs := (suggestionBoxHeight / 2) - (h / 2)
ctx.String(s.name, x+border, y+yOffs)
}
func NewCommandPalette(conf cfg.TomlConfig, view *View) *CommandPalette {
conf.Editor.Show_Line_Numbers = false
conf.Editor.Highlight_Line = false
palette := &CommandPalette{
conf: &conf,
parent: view,
buff: NewBuffer(&conf, BufferConfig{
conf.Theme.Palette.Background,
conf.Theme.Palette.Foreground,
conf.Theme.Palette.Cursor,
conf.Theme.Palette.Cursor, // TODO invert
// we dont show line numbers
// so these aren't necessary
0x0, 0x0,
}, nil, 0),
parentBuff: nil,
}
palette.buff.appendLine("")
palette.Resize(view.w/3, 48)
palette.Translate((view.w/2)-(palette.w/2), 10)
// the buffer is not rendered
// relative to the palette so we have to set its position
palette.buff.Resize(palette.w, palette.h)
palette.buff.Translate((view.w/2)-(palette.w/2), 10)
// this is technically a hack. this ex is an xoffset
// for the line numbers but we're going to use it for
// general border offsets. this is a real easy fixme
// for general code clean but maybe another day!
palette.buff.ex = 5
suggestionBoxWidth = palette.w
return palette
}
func (b *CommandPalette) processCommand() {
tokenizedLine := strings.Split(b.buff.contents[0].String(), " ")
command := tokenizedLine[0]
log.Println(tokenizedLine)
action, exists := actions[command]
if !exists {
return
}
action.proc(b.parent, tokenizedLine[1:])
}
func (b *CommandPalette) calculateSuggestions() {
tokenizedLine := strings.Split(b.buff.contents[0].String(), " ")
command := tokenizedLine[0]
if command == "" {
b.recentSuggestions = nil
return
}
ranks := fuzzy.RankFind(command, commandSet)
suggestions := []suggestion{}
for _, r := range ranks {
cmdName := commandSet[r.Index]
if cmdName == "" {
continue
}
suggestions = append(suggestions, suggestion{b, cmdName})
}
b.recentSuggestions = &suggestions
}
func (b *CommandPalette) scrollSuggestion(dir int) {
if b.recentSuggestions != nil {
b.suggestionIndex += dir
if b.suggestionIndex < 0 {
b.suggestionIndex = len(*b.recentSuggestions) - 1
} else if b.suggestionIndex >= len(*b.recentSuggestions) {
b.suggestionIndex = 0
}
}
}
func (b *CommandPalette) clearInput() {
b.buff.deleteLine()
}
func (b *CommandPalette) setToSuggested() {
if b.recentSuggestions == nil {
return
}
// set the buffer
suggestions := *b.recentSuggestions
sugg := suggestions[b.suggestionIndex]
b.buff.setLine(0, sugg.name)
// remove all suggestions
b.recentSuggestions = nil
b.suggestionIndex = -1
}
func (b *CommandPalette) OnUpdate() bool {
if !b.HasFocus() {
return false
}
override := func(key int) bool {
switch key {
case sdl.K_UP:
b.scrollSuggestion(-1)
return false
case sdl.K_DOWN:
b.scrollSuggestion(1)
return false
// any other key we calculate
// the suggested commands
default:
b.suggestionIndex = -1
b.calculateSuggestions()
return false
case sdl.K_RETURN:
// we have a suggestion so let's
// fill the buffer with that instead!
if b.suggestionIndex != -1 {
b.setToSuggested()
return true
}
b.processCommand()
break
case sdl.K_ESCAPE:
break
}
b.parent.hidePalette()
return true
}
return b.buff.processInput(override)
}
func (b *CommandPalette) OnRender(ctx *strife.Renderer) {
if !b.HasFocus() {
return
}
conf := b.conf.Theme.Palette
border := 5
ctx.SetColor(strife.HexRGB(conf.Outline))
ctx.Rect(b.x-border, b.y-border, b.w+(border*2), b.h+(border*2), strife.Fill)
_, charHeight := ctx.String("foo", -5000, -5000)
b.buff.ey = (suggestionBoxHeight / 2) - (charHeight / 2)
b.buff.OnRender(ctx)
if b.recentSuggestions != nil {
for i, sugg := range *b.recentSuggestions {
if b.suggestionIndex != i {
sugg.render(b.x, b.y+((i+1)*(suggestionBoxHeight+border)), ctx)
} else {
sugg.renderHighlighted(b.x, b.y+((i+1)*(suggestionBoxHeight+border)), ctx)
}
}
}
}

View File

@ -1,73 +0,0 @@
package gui
import (
"bytes"
"github.com/atotto/clipboard"
"io/ioutil"
"log"
)
func ShowPalette(v *View, commands []string) bool {
b := v.getCurrentBuff()
v.UnfocusBuffers()
v.focusPalette(b)
return true
}
func Paste(v *View, commands []string) bool {
b := v.getCurrentBuff()
if b == nil {
return false
}
str, err := clipboard.ReadAll()
if err == nil {
b.insertString(b.curs.x, str)
b.moveToEndOfLine()
return true
}
log.Println("Failed to paste from clipboard: ", err.Error())
return false
}
// NOTE: all shortcuts return a bool
// this is whether or not they have
// modified the buffer
// if the buffer is modified it will be
// re-rendered.
func Save(v *View, commands []string) bool {
b := v.getCurrentBuff()
if b == nil {
return false
}
var buffer bytes.Buffer
for idx, line := range b.contents {
if idx > 0 {
// TODO: this avoids a trailing newline
// if we handle it like this? but if we have
// say enforce_newline_at_eof or something we
// might want to do this all the time
buffer.WriteRune('\n')
}
buffer.WriteString(line.String())
}
// TODO:
// - files probably dont have to be entirely
// re-saved all the time!
// - we can probably stream this somehow?
// - multi threaded?
// - lots of checks to do here: does the file exist/not exist
// handle the errors... etc.
err := ioutil.WriteFile(b.filePath, buffer.Bytes(), 0775)
if err != nil {
log.Println(err.Error())
}
log.Println("Wrote file '" + b.filePath + "' to disk")
return false
}

View File

@ -1,198 +0,0 @@
package gui
import (
"github.com/felixangell/phi/cfg"
"github.com/felixangell/strife"
"github.com/veandco/go-sdl2/sdl"
"log"
"unicode"
)
// View is an array of buffers basically.
type View struct {
BaseComponent
conf *cfg.TomlConfig
buffers map[int]*Buffer
focusedBuff int
commandPalette *CommandPalette
}
func NewView(width, height int, conf *cfg.TomlConfig) *View {
view := &View{
conf: conf,
buffers: map[int]*Buffer{},
}
view.Translate(width, height)
view.Resize(width, height)
view.commandPalette = NewCommandPalette(*conf, view)
view.UnfocusBuffers()
return view
}
func (n *View) hidePalette() {
p := n.commandPalette
p.clearInput()
p.SetFocus(false)
// set focus to the buffer
// that invoked the cmd palette
if p.parentBuff != nil {
p.parentBuff.SetFocus(true)
n.focusedBuff = p.parentBuff.index
}
// remove focus from palette
p.buff.SetFocus(false)
}
func (n *View) focusPalette(buff *Buffer) {
p := n.commandPalette
p.SetFocus(true)
// focus the command palette
p.buff.SetFocus(true)
// remove focus from the buffer
// that invoked the command palette
p.parentBuff = buff
}
func (n *View) UnfocusBuffers() {
// clear focus from buffers
for _, buff := range n.buffers {
buff.SetFocus(false)
}
}
func sign(dir int) int {
if dir > 0 {
return 1
} else if dir < 0 {
return -1
}
return 0
}
func (n *View) removeBuffer(index int) {
log.Println("Removing buffer index:", index)
delete(n.buffers, index)
// only resize the buffers if we have
// some remaining in the window
if len(n.buffers) > 0 {
bufferWidth := n.w / len(n.buffers)
// translate all the components accordingly.
for i, buff := range n.buffers {
buff.Resize(bufferWidth, n.h)
buff.SetPosition(bufferWidth*i, 0)
}
}
}
func (n *View) ChangeFocus(dir int) {
prevBuff, _ := n.buffers[n.focusedBuff]
if dir == -1 {
n.focusedBuff--
} else if dir == 1 {
n.focusedBuff++
}
if n.focusedBuff < 0 {
n.focusedBuff = len(n.buffers) - 1
} else if n.focusedBuff >= len(n.buffers) {
n.focusedBuff = 0
}
prevBuff.SetFocus(false)
if buff, ok := n.buffers[n.focusedBuff]; ok {
buff.SetFocus(true)
}
}
func (n *View) getCurrentBuff() *Buffer {
if buff, ok := n.buffers[n.focusedBuff]; ok {
return buff
}
return nil
}
func (n *View) OnInit() {
}
func (n *View) OnUpdate() bool {
dirty := false
CONTROL_DOWN = strife.KeyPressed(sdl.K_LCTRL) || strife.KeyPressed(sdl.K_RCTRL)
if CONTROL_DOWN && strife.PollKeys() {
r := rune(strife.PopKey())
actionName, actionExists := cfg.Shortcuts.Controls[string(unicode.ToLower(r))]
if actionExists {
if action, ok := actions[actionName]; ok {
log.Println("Executing action '" + actionName + "'")
return action.proc(n, []string{})
}
} else {
log.Println("warning, unimplemented shortcut ctrl +", string(unicode.ToLower(r)), actionName)
}
}
if buff, ok := n.buffers[n.focusedBuff]; ok {
buff.processInput(nil)
buff.OnUpdate()
}
n.commandPalette.OnUpdate()
return dirty
}
func (n *View) OnRender(ctx *strife.Renderer) {
for _, buffer := range n.buffers {
buffer.OnRender(ctx)
}
n.commandPalette.OnRender(ctx)
}
func (n *View) OnDispose() {}
func (n *View) AddBuffer() *Buffer {
n.UnfocusBuffers()
cfg := n.conf
c := NewBuffer(cfg, BufferConfig{
cfg.Theme.Background,
cfg.Theme.Foreground,
cfg.Theme.Cursor,
cfg.Theme.Cursor_Invert,
cfg.Theme.Gutter_Background,
cfg.Theme.Gutter_Foreground,
}, n, len(n.buffers))
c.SetFocus(true)
// work out the size of the buffer and set it
// note that we +1 the components because
// we haven't yet added the panel
var bufferWidth int
bufferWidth = n.w / (c.index + 1)
n.buffers[c.index] = c
n.focusedBuff = c.index
// translate all the components accordingly.
for i, buff := range n.buffers {
buff.Resize(bufferWidth, n.h)
buff.SetPosition(bufferWidth*i, 0)
}
return c
}

6
index.html Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<h1>Hello, world!</h1>
</html>

View File

@ -1,67 +0,0 @@
package lex
type Lexer struct {
pos int
input []rune
}
func New(input string) *Lexer {
return &Lexer{
pos: 0,
input: []rune(input),
}
}
func (l *Lexer) consume() rune {
consumed := l.peek()
l.pos++
return consumed
}
func (l *Lexer) next(offs int) rune {
return l.input[l.pos+offs]
}
func (l *Lexer) peek() rune {
return l.input[l.pos]
}
func (l *Lexer) hasNext() bool {
return l.pos < len(l.input)
}
func (l *Lexer) Tokenize() []*Token {
var result []*Token
for l.hasNext() {
// TODO make it so that we can generate
// lexers from the config files
// allowing the user to put token
// matching criteria in here. for now
// we'll just go with a simple lexer
// that splits strings by spaces/tabs/etc
// skip all the layout characters
// we dont care about these.
for l.hasNext() && l.peek() <= ' ' {
l.consume()
}
startPos := l.pos
for l.hasNext() {
// we run into a layout character
if l.peek() <= ' ' {
break
}
l.consume()
}
// this should be a recognized
// token i think?
lexeme := string(l.input[startPos:l.pos])
tok := NewToken(lexeme, Word, startPos)
result = append(result, tok)
}
return result
}

View File

@ -1,23 +0,0 @@
package lex
import "fmt"
type TokenType uint
const (
Word TokenType = iota
)
type Token struct {
Lexeme string
Type TokenType
Start int
}
func NewToken(lexeme string, kind TokenType, start int) *Token {
return &Token{lexeme, kind, start}
}
func (t *Token) String() string {
return fmt.Sprintf("lexeme: %s, type %s, at pos %d", t.Lexeme, t.Type, t.Start)
}

158
main.go
View File

@ -1,158 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"runtime"
"time"
"github.com/felixangell/phi/cfg"
"github.com/felixangell/phi/gui"
"github.com/felixangell/strife"
)
const (
PRINT_FPS bool = true
)
type PhiEditor struct {
running bool
defaultFont *strife.Font
mainView *gui.View
}
func (n *PhiEditor) handleEvent(evt strife.StrifeEvent) {
}
func (n *PhiEditor) init(cfg *cfg.TomlConfig) {
mainView := gui.NewView(1280, 720, cfg)
args := os.Args
if len(args) > 1 {
// TODO check these are files
// that actually exist here?
for _, arg := range args[1:] {
mainView.AddBuffer().OpenFile(arg)
}
} else {
// we have no args, open up a scratch file
tempFile, err := ioutil.TempFile("", "phi-editor-")
if err != nil {
log.Println("Failed to create temp file", err.Error())
os.Exit(1)
}
mainView.AddBuffer().OpenFile(tempFile.Name())
}
n.mainView = mainView
n.defaultFont = cfg.Editor.Loaded_Font
}
func (n *PhiEditor) dispose() {
}
func (n *PhiEditor) update() bool {
return n.mainView.OnUpdate()
}
func (n *PhiEditor) render(ctx *strife.Renderer) {
ctx.SetFont(n.defaultFont)
n.mainView.OnRender(ctx)
}
func main() {
config := cfg.Setup()
ww, wh := 1280, 720
window := strife.SetupRenderWindow(ww, wh, strife.DefaultConfig())
window.SetTitle("Hello world!")
window.SetResizable(true)
editor := &PhiEditor{running: true}
window.HandleEvents(func(evt strife.StrifeEvent) {
switch evt.(type) {
case *strife.CloseEvent:
window.Close()
default:
editor.handleEvent(evt)
}
})
window.Create()
{
size := "16"
switch runtime.GOOS {
case "windows":
size = "256"
case "darwin":
size = "512"
case "linux":
size = "96"
default:
log.Println("unrecognized runtime ", runtime.GOOS)
}
icon, err := strife.LoadImage("./res/icons/icon" + size + ".png")
if err != nil {
log.Println("Failed to load icon ", err.Error())
} else {
window.SetIconImage(icon)
defer icon.Destroy()
}
}
editor.init(&config)
lastDebugRender := time.Now()
frames, updates := 0, 0
fps, ups := frames, updates
ctx := window.GetRenderContext()
ctx.Clear()
editor.render(ctx)
ctx.Display()
for {
window.PollEvents()
if window.CloseRequested() {
break
}
shouldRender := editor.update()
if shouldRender || config.Render.Always_Render {
ctx.Clear()
editor.render(ctx)
// this is only printed on each
// render...
ctx.SetColor(strife.White)
ctx.String(fmt.Sprintf("fps: %d, ups %d", fps, ups), ww-256, wh-128)
ctx.Display()
frames += 1
}
updates += 1
if time.Now().Sub(lastDebugRender) >= time.Second {
lastDebugRender = time.Now()
fps, ups = frames, updates
frames, updates = 0, 0
}
if config.Render.Throttle_Cpu_Usage {
// todo put in the config how long
// we sleep for!
time.Sleep(16)
}
}
editor.dispose()
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View File

@ -1,39 +0,0 @@
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx