~reesmichael1/roman

6130cac4643c7f1a75c41f1854ad340361d44a2b — Michael Rees 11 months ago 942825f
Add support for editing subscription title

This commit adds a new dependency on the noise library, a Nim
implementation of linenoise. This is being used instead of rdstdin in
the Nim standard library (which wraps linenoise) because it's nice to
give the user the current name as a default when editing, which rdstdin
doesn't allow.
M roman.nimble => roman.nimble +1 -0
@@ 17,6 17,7 @@ requires "fab >= 0.4"
requires "feednim >= 0.2"
requires "nim >= 0.20.0"
requires "nimpy >= 0.1"
requires "noise >= 0.1"




M src/romanpkg/main.nim => src/romanpkg/main.nim +8 -2
@@ 46,8 46,12 @@ proc chooseFeed(feeds: seq[Feed]): Feed {.raises: [RomanError,
proc chooseManageAction(): ManageAction {.raises: [RomanError].} =
  # TODO: implement more actions
  try:
    var displayNames = {NoOp: "do nothing", Unsubscribe: "unsubscribe"}.toTable
    let action = promptList("Choose operation", [Unsubscribe, NoOp], displayNames)
    var displayNames = {
      EditTitle: "edit title",
      NoOp: "do nothing",
      Unsubscribe: "unsubscribe"
    }.toTable
    let action = promptList("Choose operation", [EditTitle, Unsubscribe, NoOp], displayNames)
    if action.isNone:
      return NoOp
    return action.unsafeGet()


@@ 122,6 126,8 @@ proc manage*() {.raises: [].} =
      case action
      of NoOp:
        discard
      of EditTitle:
        editSubscriptionTitle(sub)
      of Unsubscribe:
        removeSubscriptionFromSubsFile(sub)
    except InterruptError:

M src/romanpkg/subscriptions.nim => src/romanpkg/subscriptions.nim +25 -6
@@ 6,10 6,16 @@ import strutils
import errors
import feeds
import paths
import termask

from types import FeedKind, Subscription


proc newSubscription*(name, url: string, kind: FeedKind): Subscription {.
    raises: [].} =
  Subscription(name: name, url: url, feedKind: kind)


proc getSubscriptions*(): seq[Subscription] {.raises: [RomanError].} =
  let subsFilePath = getSubsFilePath()
  if not existsFile(subsFilePath):


@@ 54,16 60,12 @@ proc subscriptionToLine(sub: Subscription): string {.raises: [RomanError].} =
  return sub.name & "," & sub.url & "," & kind


proc addSubscriptionToSubsFile*(url: string, feedKind: FeedKind) {.
    raises: [RomanError].} =
proc addFullSubscriptionToSubsFile(subscription: Subscription) {.raises: [RomanError].} =
  try:
    let feed = getFeed(Subscription(url: url, feedKind: feedKind))
    let subscription = Subscription(name: feed.title, url: url,
        feedKind: feed.kind)
    let subs = getSubscriptions()
    if subscription in subs:
      raise newException(RomanError,
        "you are already subscribed to " & url & "!")
        "you are already subscribed to " & subscription.url & "!")
    var f: File
    let filename = getSubsFilePath()
    if f.open(filename, fmAppend):


@@ 73,6 75,14 @@ proc addSubscriptionToSubsFile*(url: string, feedKind: FeedKind) {.
    raise newException(RomanError, e.msg)


proc addSubscriptionToSubsFile*(url: string, feedKind: FeedKind) {.
    raises: [RomanError].} =
  let feed = getFeed(Subscription(url: url, feedKind: feedKind))
  let subscription = Subscription(name: feed.title, url: url,
      feedKind: feed.kind)
  addFullSubscriptionToSubsFile(subscription)


proc subscriptionFromLine(line: string): Subscription {.raises: [RomanError].} =
  let fields = line.split(",")
  result.name = fields[0]


@@ 109,3 119,12 @@ proc removeSubscriptionFromSubsFile*(sub: Subscription) {.
  except IOError as e:
    raise newException(RomanError,
      "could not open subscriptions file: " & e.msg)


proc editSubscriptionTitle*(sub: Subscription) {.raises: [RomanError].} =
  let newName = askUserForInput("Enter new name (empty to go back): ", sub.name)
  if newName == "":
    return
  let newSub = newSubscription(newName, sub.url, sub.feedKind)
  removeSubscriptionFromSubsFile(sub)
  addFullSubscriptionToSubsFile(newSub)

M src/romanpkg/termask.nim => src/romanpkg/termask.nim +25 -4
@@ 5,11 5,33 @@ import tables
import terminal

import fab
import noise

import errors
from config import conf


# This function was originally based on the promptListInteractive function
proc askUserForInput*(prompt: string, default = ""): string {.
    raises: [RomanError].} =
  ## Read user input from stdin, but optionally provide a default value
  ## to pre-fill the prompt.
  try:
    var noise = Noise.init()
    let prompt = Styler.init(prompt)
    noise.setPrompt(prompt)
    noise.preloadBuffer(default)

    let ok = noise.readLine()
    if not ok:
      raise newException(RomanError, "could not read from input line")
    result = noise.getLine()

  except:
    let msg = getCurrentExceptionMsg()
    raise newException(RomanError, "could not ask user for input: " & msg)


# These functions were originally based on the promptListInteractive function
# in Nimble, and is therefore under the same license.

# Copyright (c) 2015, Dominik Picheta


@@ 213,9 235,8 @@ proc promptList*[T](question: string, args: openarray[T],
      else: break

  for i in 0..<currentArgs.len:
    eraseLine(stdout)
    cursorDown(stdout)
  for i in 0..<currentArgs.len():
    cursorUp(stdout)
  echo "\n"

  showCursor(stdout)
  return some(currentArgs[selectedIx])

M src/romanpkg/types.nim => src/romanpkg/types.nim +1 -1
@@ 44,7 44,7 @@ type
    url*: string

  ManageAction* = enum
    NoOp, Unsubscribe
    EditTitle, NoOp, Unsubscribe