about summary refs log tree commit diff stats
path: root/017parse_tree.cc
blob: f0130d9711c115f0d1ec8f81228bdc4262b18bc3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// So far instructions can only contain linear lists of properties. Now we add
// support for more complex trees of properties in dilated reagents. This will
// come in handy later for expressing complex types, like "a dictionary from
// (address to array of charaters) to (list of numbers)".
//
// Type trees aren't as general as s-expressions even if they look like them:
// the first element of a type tree is always an atom, and it can never be
// dotted (right->right->right->...->right is always NULL).
//
// For now you can't use the simpler 'colon-based' representation inside type
// trees. Once you start typing parens, keep on typing parens.

:(scenarios load)
:(scenario dilated_reagent_with_nested_brackets)
def main [
  {1: number, foo: (bar (baz quux))} <- copy 34
]
+parse:   product: {1: "number", "foo": ("bar" ("baz" "quux"))}

:(before "End Parsing Dilated Reagent Property(value)")
value = parse_string_tree(value);
:(before "End Parsing Dilated Reagent Type Property(type_names)")
type_names = parse_string_tree(type_names);

:(code)
string_tree* parse_string_tree(string_tree* s) {
  assert(s->atom);
  if (!starts_with(s->value, "(")) return s;
  string_tree* result = parse_string_tree(s->value);
  delete s;
  return result;
}

string_tree* parse_string_tree(const string& s) {
  istringstream in(s);
  in >> std::noskipws;
  return parse_string_tree(in);
}

string_tree* parse_string_tree(istream& in) {
  skip_whitespace_but_not_newline(in);
  if (!has_data(in)) return NULL;
  if (in.peek() == ')') {
    in.get();
    return NULL;
  }
  if (in.peek() != '(') {
    string s = next_word(in);
    if (s.empty()) {
      assert(!has_data(in));
      raise << "incomplete string tree at end of file (0)\n" << end();
      return NULL;
    }
    string_tree* result = new string_tree(s);
    return result;
  }
  in.get();  // skip '('
  string_tree* result = NULL;
  string_tree** curr = &result;
  while (true) {
    skip_whitespace_but_not_newline(in);
    assert(has_data(in));
    if (in.peek() == ')') break;
    *curr = new string_tree(NULL, NULL);
    if (in.peek() == '(') {
      (*curr)->left = parse_string_tree(in);
    }
    else {
      string s = next_word(in);
      if (s.empty()) {
        assert(!has_data(in));
        raise << "incomplete string tree at end of file (1)\n" << end();
        return NULL;
      }
      (*curr)->left = new string_tree(s);
    }
    curr = &(*curr)->right;
  }
  in.get();  // skip ')'
  assert(*curr == NULL);
  return result;
}

:(scenario dilated_reagent_with_type_tree)
% Hide_errors = true;  // 'map' isn't defined yet
def main [
  {1: (foo (address array character) (bar number))} <- copy 34
]
# just to avoid errors
container foo [
]
container bar [
]
+parse:   product: {1: ("foo" ("address" "array" "character") ("bar" "number"))}

:(scenario dilated_empty_tree)
def main [
  {1: number, foo: ()} <- copy 34
]
+parse:   product: {1: "number", "foo": ()}

:(scenario dilated_singleton_tree)
def main [
  {1: number, foo: (bar)} <- copy 34
]
+parse:   product: {1: "number", "foo": ("bar")}
0 committer Drew DeVault <sir@cmpwn.com> 2019-05-19 11:51:16 -0400 Update internal state and draw from the same goroutine' href='/akspecs/aerc/commit/widgets/msglist.go?h=0.1.3&id=a15ea01cfb0a303355b2e6bb31e85ece0d048ac2'>a15ea01 ^
312a53e ^

3cd0d5b ^
f1698a3 ^

3cd0d5b ^

312a53e ^
f1698a3 ^

312a53e ^

13ba53c ^
f1698a3 ^
a275f65 ^
13ba53c ^
f1698a3 ^
13ba53c ^
f1698a3 ^

13ba53c ^
c0146ef ^






13ba53c ^

24daef8 ^
f1698a3 ^
a275f65 ^
f1698a3 ^
0b26241 ^

24daef8 ^

24dfc47 ^
24daef8 ^
f1698a3 ^

24daef8 ^
52a97c0 ^






24daef8 ^









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195


               



                                  



                                               

 
                         






                                   
                                  

 
                                                                               
                           
                               


                                       








                                                     
                                     
                           


                                              
                                

                                                                          

                           




                                    
                                     


                                    


                                                               
 






                                                                               




                                           
                                                 
                                                   
                 
                                                        

                                                                 

                                                                     



                        
                                 
                                              



                                                           
                                   
                                                     



                                  

 



                                     
                                                             
                                

                      


                                                    

                                 
         
 


                       
                                                          
                                


                               
                        

                                 
                                              




                                  
 
                                                  
                       

 
                                     

                                                   

 
                                                      

                                                                        

 
                                          
                           
 
                           
                                                                            
         

                                             
         






                                                  

 
                                            
                           
 
                                                 

                      

                            
                               
         

                                                 
         






                                                         









                               
package widgets

import (
	"log"

	"github.com/gdamore/tcell"

	"git.sr.ht/~sircmpwn/aerc/config"
	"git.sr.ht/~sircmpwn/aerc/lib"
	"git.sr.ht/~sircmpwn/aerc/lib/ui"
	"git.sr.ht/~sircmpwn/aerc/worker/types"
)

type MessageList struct {
	ui.Invalidatable
	conf     *config.AercConfig
	logger   *log.Logger
	height   int
	scroll   int
	selected int
	spinner  *Spinner
	store    *lib.MessageStore
}

func NewMessageList(conf *config.AercConfig, logger *log.Logger) *MessageList {
	ml := &MessageList{
		conf:     conf,
		logger:   logger,
		selected: 0,
		spinner:  NewSpinner(),
	}
	ml.spinner.OnInvalidate(func(_ ui.Drawable) {
		ml.Invalidate()
	})
	// TODO: stop spinner, probably
	ml.spinner.Start()
	return ml
}

func (ml *MessageList) Invalidate() {
	ml.DoInvalidate(ml)
}

func (ml *MessageList) Draw(ctx *ui.Context) {
	ml.height = ctx.Height()
	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)

	store := ml.Store()
	if store == nil {
		ml.spinner.Draw(ctx)
		return
	}

	var (
		needsHeaders []uint32
		row          int = 0
	)

	for i := len(store.Uids) - 1 - ml.scroll; i >= 0; i-- {
		uid := store.Uids[i]
		msg := store.Messages[uid]

		if row >= ctx.Height() {
			break
		}

		if msg == nil {
			needsHeaders = append(needsHeaders, uid)
			ml.spinner.Draw(ctx.Subcontext(0, row, ctx.Width(), 1))
			row += 1
			continue
		}

		style := tcell.StyleDefault
		if row == ml.selected-ml.scroll {
			style = style.Reverse(true)
		}
		if _, ok := store.Deleted[msg.Uid]; ok {
			style = style.Foreground(tcell.ColorGray)
		}
		ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
		ctx.Printf(0, row, style, "%s", msg.Envelope.Subject)

		row += 1
	}

	if len(store.Uids) == 0 {
		msg := ml.conf.Ui.EmptyMessage
		ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
			tcell.StyleDefault, "%s", msg)
	}

	if len(needsHeaders) != 0 {
		store.FetchHeaders(needsHeaders, nil)
		ml.spinner.Start()
	} else {
		ml.spinner.Stop()
	}
}

func (ml *MessageList) Height() int {
	return ml.height
}

func (ml *MessageList) storeUpdate(store *lib.MessageStore) {
	if ml.Store() != store {
		return
	}

	if len(store.Uids) > 0 {
		for ml.selected >= len(store.Uids) {
			ml.Prev()
		}
	}

	ml.Invalidate()
}

func (ml *MessageList) SetStore(store *lib.MessageStore) {
	if ml.Store() == store {
		ml.scroll = 0
		ml.selected = 0
	}
	ml.store = store
	if store != nil {
		ml.spinner.Stop()
		store.OnUpdate(ml.storeUpdate)
	} else {
		ml.spinner.Start()
	}
	ml.Invalidate()
}

func (ml *MessageList) Store() *lib.MessageStore {
	return ml.store
}

func (ml *MessageList) Empty() bool {
	store := ml.Store()
	return store == nil || len(store.Uids) == 0
}

func (ml *MessageList) Selected() *types.MessageInfo {
	store := ml.Store()
	return store.Messages[store.Uids[len(store.Uids)-ml.selected-1]]
}

func (ml *MessageList) Select(index int) {
	store := ml.Store()

	ml.selected = index
	for ; ml.selected < 0; ml.selected = len(store.Uids) + ml.selected {
	}
	if ml.selected > len(store.Uids) {
		ml.selected = len(store.Uids)
	}
	// I'm too lazy to do the math right now
	for ml.selected-ml.scroll >= ml.Height() {
		ml.scroll += 1
	}
	for ml.selected-ml.scroll < 0 {
		ml.scroll -= 1
	}
}

func (ml *MessageList) nextPrev(delta int) {
	store := ml.Store()

	if store == nil || len(store.Uids) == 0 {
		return
	}
	ml.selected += delta
	if ml.selected < 0 {
		ml.selected = 0
	}
	if ml.selected >= len(store.Uids) {
		ml.selected = len(store.Uids) - 1
	}
	if ml.Height() != 0 {
		if ml.selected-ml.scroll >= ml.Height() {
			ml.scroll += 1
		} else if ml.selected-ml.scroll < 0 {
			ml.scroll -= 1
		}
	}
	ml.Invalidate()
}

func (ml *MessageList) Next() {
	ml.nextPrev(1)
}

func (ml *MessageList) Prev() {
	ml.nextPrev(-1)
}