/
Modes.hs
213 lines (184 loc) · 7.59 KB
/
Modes.hs
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
{-# LANGUAGE Rank2Types #-}
module Yi.Modes (TokenBasedMode, fundamentalMode,
cMode, objectiveCMode, cppMode, cabalMode,
srmcMode, ocamlMode, ottMode, gnuMakeMode,
perlMode, pythonMode, anyExtension,
extensionOrContentsMatch, linearSyntaxMode,
svnCommitMode, hookModes, applyModeHooks,
lookupMode, whitespaceMode, removeAnnots
) where
import Prelude ()
import Data.List ( isPrefixOf, map, filter )
import Data.Maybe
import System.FilePath
import Text.Regex.TDFA ((=~))
import Yi.Buffer
import Yi.Lexer.Alex (Tok(..), tokToSpan)
import Yi.Prelude
import Yi.Style
import Yi.Syntax
import Yi.Syntax.Tree
import qualified Yi.Syntax.Driver as Driver
import Yi.Keymap
import Yi.MiniBuffer
import qualified Yi.Lexer.Alex as Alex
import qualified Yi.Lexer.Cabal as Cabal
import qualified Yi.Lexer.C as C
import qualified Yi.Lexer.ObjectiveC as ObjectiveC
import qualified Yi.Lexer.Cplusplus as Cplusplus
import qualified Yi.Lexer.GNUMake as GNUMake
import qualified Yi.Lexer.OCaml as OCaml
import qualified Yi.Lexer.Ott as Ott
import qualified Yi.Lexer.Perl as Perl
import qualified Yi.Lexer.Python as Python
import qualified Yi.Lexer.Srmc as Srmc
import qualified Yi.Lexer.SVNCommit as SVNCommit
import qualified Yi.Lexer.Whitespace as Whitespace
import Yi.Syntax.OnlineTree as OnlineTree
import qualified Yi.IncrementalParse as IncrParser
type TokenBasedMode tok = Mode (Tree (Tok tok))
type StyleBasedMode = TokenBasedMode StyleName
fundamentalMode :: Mode syntax
svnCommitMode, cMode, objectiveCMode, cppMode, cabalMode, srmcMode, ottMode, gnuMakeMode, perlMode, pythonMode :: StyleBasedMode
ocamlMode :: TokenBasedMode (OCaml.Token)
fundamentalMode = emptyMode
{
modeName = "fundamental",
modeApplies = modeAlwaysApplies,
modeIndent = const autoIndentB,
modePrettify = const fillParagraph
}
linearSyntaxMode :: forall lexerState t.
(Show lexerState) =>
lexerState
-> ((IncrParser.AlexState lexerState,
Alex.AlexInput)
-> Maybe
(Tok t,
(IncrParser.AlexState lexerState,
Alex.AlexInput)))
-> (t -> StyleName)
-> Mode (Tree (Tok t))
linearSyntaxMode initSt scanToken tokenToStyle
= fundamentalMode {
modeHL = ExtHL $ Driver.mkHighlighter (IncrParser.scanner OnlineTree.manyToks . lexer),
modeGetStrokes = tokenBasedStrokes tokenToStroke
}
where tokenToStroke = fmap tokenToStyle . tokToSpan
lexer = Alex.lexScanner scanToken initSt
removeAnnots :: Mode a -> Mode a
removeAnnots m = m { modeName = modeName m ++ " no annots", modeGetAnnotations = modeGetAnnotations emptyMode }
cMode = (linearSyntaxMode C.initState C.alexScanToken id)
{
modeApplies = anyExtension ["c", "h"],
modeName = "c"
}
objectiveCMode = (linearSyntaxMode ObjectiveC.initState ObjectiveC.alexScanToken id)
{
modeApplies = anyExtension ["m"],
modeName = "objective-c"
}
cppMode = (linearSyntaxMode Cplusplus.initState Cplusplus.alexScanToken id)
{
modeApplies = anyExtension ["cxx", "cpp", "hxx"],
modeName = "c++"
}
cabalMode = (linearSyntaxMode Cabal.initState Cabal.alexScanToken id)
{
modeName = "cabal",
modeApplies = anyExtension ["cabal"],
modeToggleCommentSelection = toggleCommentSelectionB "-- " "--"
}
srmcMode = (linearSyntaxMode Srmc.initState Srmc.alexScanToken id)
{
modeName = "srmc",
modeApplies = anyExtension ["pepa", -- pepa is a subset of srmc
"srmc"]
}
svnCommitMode = (linearSyntaxMode SVNCommit.initState SVNCommit.alexScanToken id)
{
modeName = "svn-commit",
modeApplies = \path _contents -> isPrefixOf "svn-commit" path && extensionMatches ["tmp"] path
}
ocamlMode = (linearSyntaxMode OCaml.initState OCaml.alexScanToken OCaml.tokenToStyle)
{
modeName = "ocaml",
modeApplies = anyExtension ["ml", "mli", "mly", "mll", "ml4", "mlp4"]
}
perlMode = (linearSyntaxMode Perl.initState Perl.alexScanToken id)
{
modeName = "perl",
modeApplies = anyExtension ["t", "pl", "pm"]
}
pythonMode = base
{
modeName = "python",
modeApplies = anyExtension ["py"],
modeIndentSettings = (modeIndentSettings base)
{
expandTabs = True,
tabSize = 4
}
}
where base = linearSyntaxMode Python.initState Python.alexScanToken id
isMakefile :: FilePath -> String -> Bool
isMakefile path _contents = matches $ takeFileName path
where matches "Makefile" = True
matches "makefile" = True
matches "GNUmakefile" = True
matches filename = extensionMatches ["mk"] filename
-- TODO: .mk is fairly standard but are there others?
gnuMakeMode = base
{
modeName = "Makefile",
modeApplies = isMakefile,
modeIndentSettings = (modeIndentSettings base)
{
expandTabs = False,
shiftWidth = 8
}
}
where base = linearSyntaxMode GNUMake.initState GNUMake.alexScanToken id
ottMode = (linearSyntaxMode Ott.initState Ott.alexScanToken id)
{
modeName = "ott",
modeApplies = anyExtension ["ott"]
}
whitespaceMode :: TokenBasedMode StyleName
whitespaceMode = base
{
modeName = "whitespace",
modeApplies = anyExtension ["ws"],
modeIndent = \_ _ -> insertB '\t'
}
where
base = linearSyntaxMode Whitespace.initState Whitespace.alexScanToken id
-- | Determines if the file's extension is one of the extensions in the list.
extensionMatches :: [String] -> FilePath -> Bool
extensionMatches extensions fileName = extension `elem` extensions'
where extension = takeExtension fileName
extensions' = ['.' : ext | ext <- extensions]
-- | When applied to an extensions list, creates a 'Mode.modeApplies' function.
anyExtension :: [String] -> FilePath -> String -> Bool
anyExtension extensions fileName _contents
= extensionMatches extensions fileName
-- | When applied to an extensions list and regular expression pattern, creates
-- a 'Mode.modeApplies' function.
extensionOrContentsMatch :: [String] -> String -> FilePath -> String -> Bool
extensionOrContentsMatch extensions pattern fileName contents
= extensionMatches extensions fileName || contentsMatch
where contentsMatch = contents =~ pattern :: Bool
-- | Adds a hook to all matching hooks in a list
hookModes :: (AnyMode -> Bool) -> BufferM () -> [AnyMode] -> [AnyMode]
hookModes p h = map $ \am@(AnyMode m) -> if p am
then AnyMode $ m { modeOnLoad = modeOnLoad m >> h }
else am
-- | Apply a list of mode hooks to a list of AnyModes
applyModeHooks :: [(AnyMode -> Bool, BufferM ())] -> [AnyMode] -> [AnyMode]
applyModeHooks hs ms = flip map ms $ \am -> case filter (($am) . fst) hs of
[] -> am
ls -> onMode (\m -> m { modeOnLoad = foldr (>>) (modeOnLoad m) (map snd ls) }) am
-- | Check whether a mode of the same name is already in modeTable and returns the
-- original mode, if it isn't the case.
lookupMode :: AnyMode -> YiM AnyMode
lookupMode am@(AnyMode m) = fromMaybe am <$> anyModeByNameM (modeName m)