-
Notifications
You must be signed in to change notification settings - Fork 147
/
documentmap.lua
235 lines (205 loc) · 8.37 KB
/
documentmap.lua
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
-- Copyright 2014-18 Paul Kulchenko, ZeroBrane LLC; All rights reserved
local mappanel = "documentmappanel"
local markers = {CURRENT = "docmap.current", BACKGROUND = "docmap.background"}
local editormap, editorlinked
local id
local win = ide.osname == 'Windows'
local needupdate
local colors = { -- default values if no style colors are set
text = {64, 64, 64},
background = {208, 208, 208},
current = {240, 240, 230},
}
local function switchEditor(editor)
if editorlinked == editor then return end
if editormap then
if editor then
local font = editor:GetFont()
editormap:SetFont(font)
editormap:StyleSetFont(wxstc.wxSTC_STYLE_DEFAULT, font)
-- reset styles when switching the editor
local styles = ide:GetConfig().styles
markers[markers.BACKGROUND] = ide:AddMarker(markers.BACKGROUND,
wxstc.wxSTC_MARK_BACKGROUND,
styles.text.fg or colors.text,
styles.sel.bg or colors.background)
editormap:MarkerDefine(ide:GetMarker(markers.BACKGROUND))
markers[markers.CURRENT] = ide:AddMarker(markers.CURRENT,
wxstc.wxSTC_MARK_BACKGROUND,
styles.text.fg or colors.text,
styles.caretlinebg.bg or colors.current)
editormap:MarkerDefine(ide:GetMarker(markers.CURRENT))
end
editormap:MarkerDeleteAll(markers[markers.CURRENT])
editormap:MarkerDeleteAll(markers[markers.BACKGROUND])
end
if editorlinked then
-- clear the editor in case the last editor tab was closed
if editormap and not editor
and ide:GetEditorNotebook():GetPageCount() == 1 then
editormap:SetDocPointer()
end
end
if editor then
editormap:SetDocPointer(editor:GetDocPointer())
end
editorlinked = editor
end
local function screenFirstLast(e)
local firstline = e:DocLineFromVisible(e:GetFirstVisibleLine())
local linesvisible = (e:DocLineFromVisible(e:GetFirstVisibleLine()+e:LinesOnScreen()-1)
- firstline)
return firstline, math.min(e:GetLineCount(), firstline + linesvisible)
end
local function sync(e1, e2)
local firstline, lastline = screenFirstLast(e1)
e2:MarkerDeleteAll(markers[markers.BACKGROUND])
for line = firstline, lastline do
e2:MarkerAdd(line, markers[markers.BACKGROUND])
end
local currline = e1:GetCurrentLine()
e2:MarkerDeleteAll(markers[markers.CURRENT])
e2:MarkerAdd(currline, markers[markers.CURRENT])
local linesmax1 = math.max(1, e1:GetLineCount() - (lastline-firstline))
local linesmax2 = math.max(1, e2:GetLineCount() - e2:LinesOnScreen())
local line2 = firstline * linesmax2 / linesmax1
e2:SetFirstVisibleLine(e2:VisibleFromDocLine(math.floor(line2)))
-- force refresh to keep the map editor up-to-date and reduce jumpy scroll
if win then e2:Refresh() e2:Update() end
end
return {
name = "Document Map",
description = "Adds document map.",
author = "Paul Kulchenko",
version = 0.31,
dependencies = "1.30",
onRegister = function(self)
local e = wxstc.wxStyledTextCtrl(ide:GetMainFrame(), wx.wxID_ANY,
wx.wxDefaultPosition, wx.wxSize(20, 20), wx.wxBORDER_NONE)
editormap = e
local w, h = 150, 150
ide:AddPanel(e, mappanel, TR("Document Map"), function(pane)
pane:Dock():Right():TopDockable(false):BottomDockable(false)
:MinSize(w,-1):BestSize(w,-1):FloatingSize(w,h)
end)
-- remove all margins
for m = 0, 4 do e:SetMarginWidth(m, 0) end
e:SetUseHorizontalScrollBar(false)
e:SetUseVerticalScrollBar(false)
e:SetZoom(self:GetConfig().zoom or -7)
e:SetSelBackground(false, wx.wxBLACK)
e:SetCaretStyle(0) -- disable caret as it may be visible when selecting on inactive map
e:SetCursor(wx.wxCursor(wx.wxCURSOR_ARROW))
-- disable dragging from or to the map
e:Connect(wxstc.wxEVT_STC_START_DRAG, function(event) event:SetDragText("") end)
e:Connect(wxstc.wxEVT_STC_DO_DROP, function(event) event:SetDragResult(wx.wxDragNone) end)
do -- set readonly when switched to
local ro
e:Connect(wx.wxEVT_SET_FOCUS, function() ro = e:GetReadOnly() e:SetReadOnly(true) end)
e:Connect(wx.wxEVT_KILL_FOCUS, function() e:SetReadOnly(ro or false) end)
end
local function setFocus(editor)
editor:SetFocus()
if ide.osname == 'Macintosh' then
-- manually trigger KILL_FOCUS event on OSX: http://trac.wxwidgets.org/ticket/14142
editormap:GetEventHandler():ProcessEvent(wx.wxFocusEvent(wx.wxEVT_KILL_FOCUS))
end
end
local function jumpLinked(point)
local pos = e:PositionFromPoint(point)
local firstline, lastline = screenFirstLast(editorlinked)
local onscreen = lastline-firstline
local topline = math.floor(e:LineFromPosition(pos)-onscreen/2)
editorlinked:SetFirstVisibleLine(editorlinked:VisibleFromDocLine(topline))
end
local scroll
local function scrollLinked(point)
local onscreen = math.min(editorlinked:LinesOnScreen(), editorlinked:GetLineCount())
local line = e:LineFromPosition(e:PositionFromPoint(point))
local lineheight = e:TextHeight(line)
local count = e:GetLineCount()
local height = math.min(count * lineheight, e:GetClientSize():GetHeight())
local scrollnow = (point:GetY() - scroll) / (height - onscreen * lineheight)
local topline = math.floor((count-onscreen)*scrollnow)
editorlinked:SetFirstVisibleLine(editorlinked:VisibleFromDocLine(topline))
end
e:Connect(wx.wxEVT_LEFT_DOWN, function(event)
if not editorlinked then return end
local point = event:GetPosition()
local pos = e:PositionFromPoint(point)
local line = e:LineFromPosition(pos)
local firstline, lastline = screenFirstLast(editorlinked)
if line >= firstline and line <= lastline then
scroll = (line-firstline) * e:TextHeight(line)
if win then e:CaptureMouse() end
else
jumpLinked(point)
setFocus(editorlinked)
end
end)
e:Connect(wx.wxEVT_LEFT_UP, function(event)
if not editorlinked then return end
if scroll then
scroll = nil
setFocus(editorlinked)
if win then e:ReleaseMouse() end
end
end)
e:Connect(wx.wxEVT_MOTION, function(event)
if not editorlinked then return end
if scroll then scrollLinked(event:GetPosition()) end
end)
-- ignore all double click events as they cause selection in the editor
e:Connect(wx.wxEVT_LEFT_DCLICK, function(event) end)
-- ignore context menu
e:Connect(wx.wxEVT_CONTEXT_MENU, function(event) end)
-- set the cursor so it doesn't look like vertical beam
e:Connect(wx.wxEVT_SET_CURSOR, function(event)
event:SetCursor(wx.wxCursor(wx.wxCURSOR_ARROW))
end)
local menu = ide:FindTopMenu("&View")
id = ID("documentmap.documentmapview")
menu:InsertCheckItem(4, id, TR("Document Map Window")..KSC(id))
ide:GetMainFrame():Connect(id, wx.wxEVT_COMMAND_MENU_SELECTED, function (event)
local uimgr = ide:GetUIManager()
uimgr:GetPane(mappanel):Show(not uimgr:GetPane(mappanel):IsShown())
uimgr:Update()
end)
ide:GetMainFrame():Connect(id, wx.wxEVT_UPDATE_UI, function (event)
local pane = ide:GetUIManager():GetPane(mappanel)
ide:GetMenuBar():Enable(event:GetId(), pane:IsOk()) -- disable if doesn't exist
ide:GetMenuBar():Check(event:GetId(), pane:IsOk() and pane:IsShown())
end)
end,
onUnRegister = function(self)
switchEditor()
ide:RemoveMenuItem(id)
ide:RemovePanel(mappanel)
end,
onEditorFocusSet = function(self, editor)
if editorlinked ~= editor then
switchEditor(editor)
-- fix markers in the editor, otherwise they are shown as default markers
editor:MarkerDefine(markers[markers.CURRENT], wxstc.wxSTC_MARK_EMPTY)
editor:MarkerDefine(markers[markers.BACKGROUND], wxstc.wxSTC_MARK_EMPTY)
local doc = ide:GetDocument(editor)
if editormap and doc then editor.SetupKeywords(editormap, doc:GetFileExt()) end
needupdate = true
end
end,
onEditorClose = function(self, editor)
if editor == editorlinked then switchEditor() end
end,
onEditorUpdateUI = function(self, editor)
needupdate = true
end,
onEditorPainted = function(self, editor)
if editormap and editor == editorlinked and needupdate then
needupdate = false
sync(editorlinked, editormap)
end
end,
}
--[[ configuration example:
documentmap = {zoom = -7} -- zoom can be set from -10 (smallest) to 0 (normal)
--]]