Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: 

 

# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> 

# 

# This file is part of qutebrowser. 

# 

# qutebrowser is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# qutebrowser is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the GNU General Public License 

# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. 

 

"""Parser for line-based files like histories.""" 

 

import os 

import os.path 

import contextlib 

 

from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject 

 

from qutebrowser.utils import log, utils, qtutils 

from qutebrowser.config import config 

 

 

class BaseLineParser(QObject): 

 

"""A LineParser without any real data. 

 

Attributes: 

_configdir: Directory to read the config from, or None. 

_configfile: The config file path. 

_fname: Filename of the config. 

_binary: Whether to open the file in binary mode. 

 

Signals: 

changed: Emitted when the history was changed. 

""" 

 

changed = pyqtSignal() 

 

def __init__(self, configdir, fname, *, binary=False, parent=None): 

"""Constructor. 

 

Args: 

configdir: Directory to read the config from. 

fname: Filename of the config file. 

binary: Whether to open the file in binary mode. 

_opened: Whether the underlying file is open 

""" 

super().__init__(parent) 

self._configdir = configdir 

self._configfile = os.path.join(self._configdir, fname) 

self._fname = fname 

self._binary = binary 

self._opened = False 

 

def __repr__(self): 

return utils.get_repr(self, constructor=True, 

configdir=self._configdir, fname=self._fname, 

binary=self._binary) 

 

def _prepare_save(self): 

"""Prepare saving of the file. 

 

Return: 

True if the file should be saved, False otherwise. 

""" 

if not os.path.exists(self._configdir): 

os.makedirs(self._configdir, 0o755) 

return True 

 

def _after_save(self): 

"""Log a message after saving is done.""" 

log.destroy.debug("Saved to {}".format(self._configfile)) 

 

@contextlib.contextmanager 

def _open(self, mode): 

"""Open self._configfile for reading. 

 

Args: 

mode: The mode to use ('a'/'r'/'w') 

 

Raises: 

IOError: if the file is already open 

 

Yields: 

a file object for the config file 

""" 

assert self._configfile is not None 

if self._opened: 

raise IOError("Refusing to double-open LineParser.") 

self._opened = True 

try: 

if self._binary: 

with open(self._configfile, mode + 'b') as f: 

yield f 

else: 

with open(self._configfile, mode, encoding='utf-8') as f: 

yield f 

finally: 

self._opened = False 

 

def _write(self, fp, data): 

"""Write the data to a file. 

 

Args: 

fp: A file object to write the data to. 

data: The data to write. 

""" 

if not data: 

return 

if self._binary: 

fp.write(b'\n'.join(data)) 

fp.write(b'\n') 

else: 

fp.write('\n'.join(data)) 

fp.write('\n') 

 

def save(self): 

"""Save the history to disk.""" 

raise NotImplementedError 

 

def clear(self): 

"""Clear the contents of the file.""" 

raise NotImplementedError 

 

 

class LineParser(BaseLineParser): 

 

"""Parser for configuration files which are simply line-based. 

 

Attributes: 

data: A list of lines. 

""" 

 

def __init__(self, configdir, fname, *, binary=False, parent=None): 

"""Constructor. 

 

Args: 

configdir: Directory to read the config from. 

fname: Filename of the config file. 

binary: Whether to open the file in binary mode. 

""" 

super().__init__(configdir, fname, binary=binary, parent=parent) 

if not os.path.isfile(self._configfile): 

self.data = [] 

else: 

log.init.debug("Reading {}".format(self._configfile)) 

self._read() 

 

def __iter__(self): 

return iter(self.data) 

 

def __getitem__(self, key): 

return self.data[key] 

 

def _read(self): 

"""Read the data from self._configfile.""" 

with self._open('r') as f: 

if self._binary: 

self.data = [line.rstrip(b'\n') for line in f] 

else: 

self.data = [line.rstrip('\n') for line in f] 

 

def save(self): 

"""Save the config file.""" 

if self._opened: 

raise IOError("Refusing to double-open LineParser.") 

do_save = self._prepare_save() 

if not do_save: 

return 

self._opened = True 

try: 

assert self._configfile is not None 

with qtutils.savefile_open(self._configfile, self._binary) as f: 

self._write(f, self.data) 

finally: 

self._opened = False 

self._after_save() 

 

def clear(self): 

self.data = [] 

self.save() 

 

 

class LimitLineParser(LineParser): 

 

"""A LineParser with a limited count of lines. 

 

Attributes: 

_limit: The config option used to limit the maximum number of lines. 

""" 

 

def __init__(self, configdir, fname, *, limit, binary=False, parent=None): 

"""Constructor. 

 

Args: 

configdir: Directory to read the config from, or None. 

fname: Filename of the config file. 

limit: Config option which contains a limit. 

binary: Whether to open the file in binary mode. 

""" 

super().__init__(configdir, fname, binary=binary, parent=parent) 

self._limit = limit 

212 ↛ exitline 212 didn't return from function '__init__', because the condition on line 212 was never false if limit is not None and configdir is not None: 

config.instance.changed.connect(self._cleanup_file) 

 

def __repr__(self): 

return utils.get_repr(self, constructor=True, 

configdir=self._configdir, fname=self._fname, 

limit=self._limit, binary=self._binary) 

 

@pyqtSlot(str) 

def _cleanup_file(self, option): 

"""Delete the file if the limit was changed to 0.""" 

assert self._configfile is not None 

if option != self._limit: 

return 

value = config.instance.get(option) 

if value == 0: 

if os.path.exists(self._configfile): 

os.remove(self._configfile) 

 

def save(self): 

"""Save the config file.""" 

limit = config.instance.get(self._limit) 

if limit == 0: 

return 

do_save = self._prepare_save() 

if not do_save: 

return 

assert self._configfile is not None 

with qtutils.savefile_open(self._configfile, self._binary) as f: 

self._write(f, self.data[-limit:]) 

self._after_save()