index page
@ -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
@ -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
|
7
LICENSE
@ -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
@ -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)
|
134
cfg/config.go
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
@ -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"
|
||||
`
|
@ -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"
|
||||
`
|
@ -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"
|
||||
`
|
258
cfg/loader.go
@ -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
|
||||
}
|
120
cfg/syntax.go
@ -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!
|
||||
}
|
@ -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
|
||||
}),
|
||||
}
|
1084
gui/buffer.go
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
258
gui/palette.go
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
198
gui/view.go
@ -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
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
</html>
|
67
lex/lex.go
@ -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
|
||||
}
|
23
lex/token.go
@ -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
@ -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()
|
||||
}
|
BIN
res/firacode.ttf
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.0 KiB |
BIN
screenshot.png
Before Width: | Height: | Size: 201 KiB |
@ -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
|