Skip to content

Commit

Permalink
Add local logging for debugging
Browse files Browse the repository at this point in the history
Logging can be enabled in the log viewer application by
pressing the 'e' key or compiling the program with the
option -d:enable_log.
  • Loading branch information
can-lehmann committed Aug 2, 2020
1 parent 146de1e commit 007245c
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 21 deletions.
3 changes: 2 additions & 1 deletion autocomplete/autocomplete.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
# SOFTWARE.

import tables, unicode, algorithm
import ../buffer, ../utils, ../highlight/highlight
import ../buffer, ../utils, ../highlight/highlight, ../log

type
SimpleContext* = ref object of Autocompleter
comps*: seq[Completion]
defs*: Table[TokenKind, DefKind]
log*: Log

method track(ctx: SimpleContext, buffer: Buffer) =
discard
Expand Down
14 changes: 9 additions & 5 deletions autocomplete/comp_nim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import unicode, strutils, os, tables, sets, hashes, streams
import sugar, sequtils, times, osproc, deques, nativesockets
import asyncdispatch, asyncnet, asyncfile, selectors
import ../buffer, ../utils
import ../buffer, ../utils, ../log

type
CaseStyle = enum CaseUnknown, CaseCamel, CaseSnake, CasePascal
Expand All @@ -43,6 +43,7 @@ type

Context = ref ContextObj
ContextObj = object of Autocompleter
log: Log
folder: string
file_id: int

Expand Down Expand Up @@ -180,7 +181,7 @@ proc save_temp_buffer(ctx: Context, buffer: Buffer): Future[string] {.async.} =
except IOError, FutureError, OSError:
return ""

proc make_context(): Context =
proc make_context(log: Log): Context =
result = Context(
triggers: @[
Rune('.'), Rune('('), Rune('[')
Expand All @@ -193,7 +194,8 @@ proc make_context(): Context =
Rune('@'),
Rune(')'), Rune('}'), Rune(']'), Rune('{')
],
min_word_len: 5
min_word_len: 5,
log: log
)
when not defined(windows):
result.stdout_selector = new_selector[pointer]()
Expand Down Expand Up @@ -281,6 +283,7 @@ proc read_port(ctx: Context) =
proc restart_nimsuggest(ctx: Context) =
if ctx.is_restarting and ctx.nimsuggest.running:
return
ctx.log.add_warning("comp_nim", "Restarting nimsuggest process")

if ctx.nimsuggest != nil:
ctx.nimsuggest.close()
Expand Down Expand Up @@ -376,7 +379,8 @@ method buffer_info(ctx: Context, buffer: Buffer): seq[string] =
of CaseSnake: return @["Snake Case"]
of CasePascal: return @["Pascal Case"]

proc make_nim_autocompleter*(): Autocompleter =
proc make_nim_autocompleter*(log: Log): Autocompleter =
if find_exe("nimsuggest") == "":
log.add_error("comp_nim", "Could not find nimsuggest")
return nil
return make_context()
return make_context(log)
12 changes: 7 additions & 5 deletions autocomplete/comp_simple.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
# SOFTWARE.

import tables, unicode, sequtils, sugar
import ../utils, ../buffer, ../highlight/highlight, autocomplete
import ../utils, ../buffer, ../highlight/highlight, autocomplete, ../log

proc new_markdown_autocompleter*(): Autocompleter =
proc new_markdown_autocompleter*(log: Log): Autocompleter =
SimpleContext(
defs: to_table({
TokenHeading: DefHeading
})
}),
log: log
)

const HTML_TAGS = map(@[
Expand All @@ -39,7 +40,7 @@ const HTML_TAGS = map(@[
"noscript", "textarea"
], tag => Completion(kind: CompTag, text: to_runes(tag)))

proc new_html_autocompleter*(): Autocompleter =
proc new_html_autocompleter*(log: Log): Autocompleter =
SimpleContext(
defs: to_table({
TokenTag: DefTag
Expand All @@ -52,7 +53,8 @@ proc new_html_autocompleter*(): Autocompleter =
Rune(' '), Rune('\n'), Rune('\r'), Rune('\t'),
Rune('='), Rune('>')
],
min_word_len: 3
min_word_len: 3,
log: log
)


4 changes: 2 additions & 2 deletions buffer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# SOFTWARE.

import strutils, unicode, sequtils, sugar, tables
import utils, highlight/highlight
import utils, highlight/highlight, log

type
DefKind* = enum
Expand Down Expand Up @@ -62,7 +62,7 @@ type
file_exts*: seq[string]
indent_width*: int
snippets*: Table[seq[Rune], seq[Rune]]
make_autocompleter*: proc (): Autocompleter
make_autocompleter*: proc (log: Log): Autocompleter

ActionKind = enum ActionDelete, ActionInsert

Expand Down
67 changes: 67 additions & 0 deletions log.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# MIT License
#
# Copyright (c) 2019 - 2020 pseudo-random <josh.leh.2018@gmail.com>
#
# 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.

import times

type
LogLevel* = enum
LogInfo, LogWarning, LogError

LogEntry* = object
level*: LogLevel
module*: string
message*: string
time*: Time

Log* = ref object
history*: seq[LogEntry]
enabled*: bool

proc add*(log: Log, entry: LogEntry) =
if log.enabled:
log.history.add(entry)
log.history[^1].time = get_time()

proc add_error*(log: Log, module, message: string) =
log.add(LogEntry(level: LogError, module: module, message: message))

proc add_warning*(log: Log, module, message: string) =
log.add(LogEntry(level: LogWarning, module: module, message: message))

proc add_info*(log: Log, module, message: string) =
log.add(LogEntry(level: LogInfo, module: module, message: message))

proc enable*(log: Log) =
log.enabled = true
log.add_info("log", "Enable log")

proc disable*(log: Log) =
log.add_info("log", "Disable log")
log.enabled = false

proc clear*(log: Log) =
log.history = @[]

proc make_log*(): Log =
result = Log()
when defined(enable_log):
result.enable()
134 changes: 134 additions & 0 deletions log_viewer.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# MIT License
#
# Copyright (c) 2019 - 2020 pseudo-random <josh.leh.2018@gmail.com>
#
# 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.

import unicode, strutils, sequtils, sugar, math, times
import utils, ui_utils, window_manager, termdiff, log

type
Viewer = ref object of Window
app: App
scroll: int
page_height: int
attach_end: bool

method process_mouse(viewer: Viewer, mouse: Mouse): bool =
let sidebar_width = len($viewer.app.log.history.len) + 1
if mouse.y == 0 and mouse.x < sidebar_width:
return true

proc display(time: Time): string =
align($time.local().hour, 2, '0') & ":" &
align($time.local().minute, 2, '0') & ":" &
align($time.local().second, 2, '0')

const LOG_NAMES: array[LogLevel, string] = ["info", "warning", "error"]

proc `$`(entry: LogEntry): string =
result = "[" & display(entry.time) & "]"
result &= "[" & LOG_NAMES[entry.level] & "]"
result &= "[" & entry.module & "] " & entry.message

proc limit_scroll(viewer: Viewer) =
viewer.scroll = min(viewer.scroll, viewer.app.log.history.len - 1)
viewer.scroll = max(viewer.scroll, 0)

proc goto_end(viewer: Viewer) =
viewer.scroll = viewer.app.log.history.len - viewer.page_height + 1
viewer.limit_scroll()

method process_key(viewer: Viewer, key: Key) =
var detach_end = true
case key.kind:
of KeyArrowDown:
viewer.scroll += 1
of KeyArrowUp:
viewer.scroll -= 1
of KeyPageDown:
viewer.scroll += viewer.page_height
of KeyPageUp:
viewer.scroll -= viewer.page_height
of KeyEnd:
viewer.attach_end = true
detach_end = false
of KeyHome:
viewer.scroll = 0
of KeyChar:
case key.chr:
of 'c':
if key.ctrl:
var lines: seq[string]
for entry in viewer.app.log.history:
lines.add($entry)
viewer.app.copy_buffer.copy(lines.join("\n").to_runes())
else:
viewer.app.log.clear()
of 'e': viewer.app.log.enable()
of 'd': viewer.app.log.disable()
else: discard
detach_end = false
else: detach_end = false

if detach_end:
viewer.attach_end = false
if viewer.attach_end:
viewer.goto_end()
viewer.limit_scroll()

const LOG_COLORS: array[LogLevel, Color] = [
Color(base: ColorDefault),
Color(base: ColorYellow),
Color(base: ColorRed, bright: true)
]

proc display(entry: LogEntry): string =
return "[" & entry.module & "] " & entry.message

method render(viewer: Viewer, box: Box, ren: var TermRenderer) =
if viewer.attach_end:
viewer.goto_end()

viewer.page_height = box.size.y
let sidebar_width = len($viewer.app.log.history.len) + 1
var log_status = "Logging Disabled"
if viewer.app.log.enabled:
log_status = "Logging Enabled"
render_border("Log Viewer (" & log_status & ")", sidebar_width, box, ren)
var
it = viewer.scroll
y = 1
while it < viewer.app.log.history.len and y < box.size.y:
ren.move_to(box.min + Index2d(y: y))
ren.put(strutils.align($it, sidebar_width),
fg=Color(base: ColorBlack),
bg=Color(base: ColorWhite)
)
ren.move_to(box.min + Index2d(x: sidebar_width + 1, y: y))
let entry = viewer.app.log.history[it]
ren.put(display(entry), fg = LOG_COLORS[entry.level])
let time_str = display(entry.time)
ren.move_to(box.min + Index2d(x: box.size.x - len(time_str), y: y))
ren.put(time_str)
y += 1
it += 1

proc make_log_viewer*(app: App): Window =
Viewer(app: app, attach_end: true)
5 changes: 3 additions & 2 deletions main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import unicode, tables, os
import termdiff, window_manager, buffer
import editor, keyinfo, calc, file_manager
import editor, keyinfo, calc, file_manager, log_viewer
import highlight/lisp, highlight/json
import highlight/html, highlight/markdown
import highlight/cpp
Expand Down Expand Up @@ -99,7 +99,8 @@ var
make_window_constructor("Editor", make_editor),
make_window_constructor("File Manager", make_file_manager),
make_window_constructor("Calc", make_calc),
make_window_constructor("Keyinfo", make_keyinfo)
make_window_constructor("Keyinfo", make_keyinfo),
make_window_constructor("Log Viewer", make_log_viewer)
]

var
Expand Down
14 changes: 8 additions & 6 deletions window_manager.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import strutils, tables, unicode, sequtils, sugar, hashes
import utils, ui_utils, termdiff, buffer
import strutils, tables, unicode, sequtils, sugar, hashes, times
import utils, ui_utils, termdiff, buffer, log

type
Window* = ref object of RootObj
Expand Down Expand Up @@ -51,9 +51,9 @@ type
WindowConstructor* = object
name: string
make: proc (app: App): Window

AppMode* = enum AppModeNone, AppModePane, AppModeNewPane

App* = ref object
root_pane*: Pane
copy_buffer*: CopyBuffer
Expand All @@ -62,6 +62,7 @@ type
mode*: AppMode
buffers*: Table[string, Buffer]
autocompleters*: Table[int, Autocompleter]
log*: Log

method process_key*(window: Window, key: Key) {.base.} = quit "Not implemented: process_key"
method process_mouse*(window: Window, mouse: Mouse): bool {.base.} = discard
Expand Down Expand Up @@ -445,7 +446,8 @@ proc make_app*(languages: seq[Language], window_constructors: seq[WindowConstruc
root_pane: nil,
languages: languages,
window_constructors: window_constructors,
buffers: init_table[string, Buffer]()
buffers: init_table[string, Buffer](),
log: make_log()
)

proc get_autocompleter*(app: App, language: Language): Autocompleter =
Expand All @@ -454,7 +456,7 @@ proc get_autocompleter*(app: App, language: Language): Autocompleter =
if language.make_autocompleter.is_nil:
return nil
if language.id notin app.autocompleters:
let autocompleter = language.make_autocompleter()
let autocompleter = language.make_autocompleter(app.log)
if autocompleter.is_nil:
return nil
app.autocompleters[language.id] = autocompleter
Expand Down

0 comments on commit 007245c

Please sign in to comment.