emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* Personal wiki
@ 2011-07-23 21:25 Anders Waldenborg
  2011-07-24 19:22 ` Bastien
  0 siblings, 1 reply; 5+ messages in thread
From: Anders Waldenborg @ 2011-07-23 21:25 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 635 bytes --]

Maybe someone else is interested in my "personal wiki" I implemented
on top of org-mode.

It basically adds three things to plain org-mode
* Narrowing
  Makes only current section[*] visible and thus avoids any distraction
  by unrelated things.
* Automatic links of all text matching a section name.
  Much like org-wikinodes, but without camel case.
* Navigation history
  When visiting different sections in the same file history is
  recorded allowing navigation "back" to previous section.

Find the el file attached, and also an example file.

What do you think?

[*] A section is the contents of a first level heading.

 anders

[-- Attachment #2: aw-org-pw.el --]
[-- Type: text/plain, Size: 10864 bytes --]

;;;; aw-org-pw.el --- personal wiki major mode derived from org-mode.
;;
;; Copyright (C) 2011 Anders Waldenborg
;;
;; Author: Anders Waldenborg <anders@0x63.nu>
;; Keywords: outlines, calendar, wp
;; Version: 0.2
;;
;; This file NOT is part of GNU Emacs.
;;
;; This program 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.
;;
;; This program 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 this program.  If not, see `http://www.gnu.org/licenses/'.
;;
;;
;;; Commentary:
;;
;; This implements the following features on top of org-mode making it
;; feel more like a (personal) wiki:
;;
;;  * Auto narrowing to current section (= first level heading).
;;  * Linking of all section names.
;;  * Creating of new sections when following nonexistant links.
;;  * Recording navigation history and back functionality.
;;
;;
;;; Change Log:
;;
;; 2011-07-23  Anders Waldenborg
;;             0.2 Bugfixes
;;                 - Fix bug with narrowing going one char to long.
;;                 - Make aw-org-pw-link-regexp buffer local.
;;                 - Fix rear-nonstickyness.
;;                 - Give aw-org-pw-current-section correct capitalization.
;;
;; 2011-07-15  Anders Waldenborg
;;             0.1 Initial release.
;;; Code:


(require 'org)

(defconst aw-org-pw-frontpage-name "StartPage"
  "Fake name created for the section before first heading")


;;;;
;;;; Random helper functions
;;;;

(defun aw-truncate-list (l n)
  "Destructivly truncates list l to n elements"
  (let ((e (nthcdr (1- n) l)))
    (when e
      (setcdr e nil)))
  l)

(defun aw-org-pw-word-at-point ()
  "Return word at point, or currently marked text if mark is active"
  (interactive)
  (if mark-active
      (buffer-substring-no-properties (point) (mark))
    (when (looking-at "\\w")
      (let ((start (save-excursion (while (not (looking-at "\\b\\w")) (backward-char)) (point)))
            (end (save-excursion (while (not (looking-at "\\w\\b")) (forward-char)) (1+ (point)))))
        (buffer-substring-no-properties start end)))))


;;;;
;;;; Section navigation
;;;;

(defun aw-org-pw-section-regexp (&optional pagename)
  "Return a regexp matching page section (or any section if pagename is nil)"
  (format "^\\* *\\(%s\\)$" (if pagename
                                (regexp-quote pagename)
                              "[^*].*")))

(defun aw-org-pw-get-section-region (name)
  "Find (start . end) points of specified section"
  (let ((case-fold-search t)
	(search (aw-org-pw-section-regexp name)))
    (save-excursion
      (save-restriction
        (widen)
        (goto-char (point-min))
        (aw-org-pw-next-section)
        (if (string-equal name aw-org-pw-frontpage-name)
            (cons (point-min) (1- (point)))
          (while (not (looking-at search))
            (forward-line)
            (aw-org-pw-next-section)
            (if (eobp)
                (error "Section not found")))
          (forward-line)
          (let ((start (point)))
            (aw-org-pw-next-section)
	    (backward-char)
            (cons start (point))))))))

(defun aw-org-pw-all-sections ()
  "Return a list with all section names"
  (save-restriction
    (save-excursion
      (widen)
      (let ((res (list aw-org-pw-frontpage-name))
            (re (aw-org-pw-section-regexp)))
        (goto-char (point-min))
        (while (not (eobp))
          (when (looking-at re)
            (setq res (cons (match-string-no-properties 1) res)))
          (forward-line))
        res))))

(defun aw-org-pw-next-section ()
  "Go to next section"
  (let ((re (aw-org-pw-section-regexp)))
    (while (not (or (eobp) (looking-at re)))
      (forward-line))
    (point)))

(defun aw-org-pw-current-section-name ()
  "Find current section name

This is usually same as the variable aw-org-pw-current-section,
but if navigation has happened by other means (e.g isearch) this
function may be needed to get the correct value."
  (save-restriction
    (save-excursion
      (widen)
      (let ((re (aw-org-pw-section-regexp)))
        (while (not (or (bobp) (looking-at re)))
          (forward-line -1))
        (if (bobp)
            aw-org-pw-frontpage-name
          (match-string-no-properties 1))))))


;;;;
;;;; Navigation with history
;;;;

(defun aw-org-pw-back ()
  "Goto previously visited section"
  (interactive)
  (unless aw-org-pw-breadcrumbs-list
    (error "Nowhere to go I think..."))
  (let ((f (pop aw-org-pw-breadcrumbs-list)))
    (aw-org-pw-goto-section-raw (car f))
    (setq aw-org-pw-current-section (car f))
    (goto-char (+ (point-min) (cdr f)))))

(defun aw-org-pw-goto-section-raw (name)
  "Go to specified section without updating history"
  (let ((reg (aw-org-pw-get-section-region name)))
    (narrow-to-region (car reg) (cdr reg))))

(defun aw-org-pw-goto-section-with-history (name offset prevsec)
  "Go to specified section recording provided history"
  (aw-org-pw-goto-section-raw name)
  (push (cons prevsec offset) aw-org-pw-breadcrumbs-list)
  (aw-truncate-list aw-org-pw-breadcrumbs-list 10)
  ; use aw-org-pw-current-section-name function instead of provided
  ; name to get correct cApiTaliZation.
  (setq aw-org-pw-current-section (aw-org-pw-current-section-name))
  (goto-char (point-min)))

(defun aw-org-pw-goto-section (name)
  "Go to specified section"
  (aw-org-pw-goto-section-with-history name (- (point) (point-min)) aw-org-pw-current-section))

(defun aw-org-pw-frontpage ()
  "Navigate to front page"
  (interactive)
  (aw-org-pw-goto-section aw-org-pw-frontpage-name))


;;;;
;;;; Links
;;;;

(defun aw-org-pw-follow-link-at-point ()
  ""
  (interactive)
  (cond
   ;; aw-org-pw link -> aw-org-pw-goto-section
   ((get-text-property (point) 'aw-org-pw-linked-text)
    (aw-org-pw-goto-section (get-text-property (point) 'aw-org-pw-linked-text)))

   ;; other kind of org link -> org-open-at-point
   ((eq (get-text-property (point) 'face) 'org-link)
    (org-open-at-point))

   ;; not a link? -> offer to create page
   ((aw-org-pw-word-at-point) (aw-org-pw-create-new-section t))

   ;; bail out
   (t (message "Don't know what to do"))))

(defun aw-org-pw-follow-link-at-mouse (ev)
  "Follow link at mouse"
  (interactive "e")
  (mouse-set-point ev)
  (aw-org-pw-follow-link-at-point))

(defun aw-org-pw-create-new-section (&optional confirm)
  "Create a new section in current file"
  (let ((pagename (aw-org-pw-word-at-point)))
    (if (and confirm (not (y-or-n-p (format "Create new page '%s'? " pagename))))
        (message "Page creation aborted")
      (widen)
      (goto-char (point-max))
      (insert "\n\n* " pagename "\n")
      (aw-org-pw-update-sections-regexp)
      (aw-org-pw-goto-section pagename))))


;;;;
;;;; Link hilighting
;;;;

(defun aw-org-pw-activate-links (limit)
  ""
  (when (derived-mode-p 'aw-org-pw-mode)
    (let ((case-fold-search t))
      (when (re-search-forward aw-org-pw-link-regexp limit t)
	(org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0))
	(add-text-properties (match-beginning 0) (match-end 0)
			     (list 'mouse-face 'highlight
				   'keymap aw-org-pw-link-map
				   'help-echo "Personal wiki link"
				   'aw-org-pw-linked-text (match-string-no-properties 0)))
	;; this is basically org-rear-nonsticky-at
	(let ((pos (match-end 0)))
	  (add-text-properties (1- pos) pos (list 'rear-nonsticky (cons 'aw-org-pw-linked-text org-nonsticky-props))))
	t))))

(add-hook 'org-font-lock-set-keywords-hook
          (lambda ()
            (add-to-list 'org-font-lock-extra-keywords
                         '(aw-org-pw-activate-links (0 'org-link t))
                         t)))

(defun aw-org-pw-update-sections-regexp ()
  "Update regexp used to create all section-links"
  (setq aw-org-pw-link-regexp (regexp-opt (aw-org-pw-all-sections) 'words)))


;;;;
;;;; isearch integration - widen before search starts, and narrow when
;;;;                       done, possibly updating breadcrumbs.
;;;;

(defvar aw-org-pw-isearch-startoff nil
  "Internal variable for storing offset in page when search starts")

(defun aw-org-pw-isearch-start ()
  (setq aw-org-pw-isearch-startoff (- (point) (point-min)))
  (widen))

(defun aw-org-pw-isearch-end ()
  (if isearch-mode-end-hook-quit
      (aw-org-pw-goto-section-raw aw-org-pw-current-section)
    (save-excursion
      (aw-org-pw-goto-section-with-history (aw-org-pw-current-section-name) aw-org-pw-isearch-startoff aw-org-pw-current-section))))


;;;;
;;;; imenu integration
;;;;

(defun aw-org-pw-imenu-entries ()
  (mapcar (lambda (e)
            (cons e 1))
          (aw-org-pw-all-sections)))

(defun aw-org-pw-imenu-goto (name pos &optional rest)
  (aw-org-pw-goto-section name))

;;;;
;;;; Avoid creating top level headers
;;;;
(defun aw-org-star ()
  "Insert two (on beginning of line) or one (otherwise) '*' characters

This works mostly as a reminder that first level headings are
special in aw-org-pw-mode and should not be created manually."
  (interactive)
  (if (bolp)
      (insert "**")
    (insert "*")))

;;;;
;;;; Actual mode definition
;;;;

(define-derived-mode aw-org-pw-mode org-mode "Org-PW" nil
  (make-local-variable 'aw-org-pw-breadcrumbs-list)
  (make-local-variable 'aw-org-pw-current-section)
  (make-local-variable 'aw-org-pw-link-regexp)

  (setq aw-org-pw-breadcrumbs-list nil)
  (setq aw-org-pw-current-section "none")

  (setq header-line-format '("[" aw-org-pw-current-section "] back:" (:eval (if aw-org-pw-breadcrumbs-list (caar aw-org-pw-breadcrumbs-list) "no history"))))

  (setq imenu-create-index-function 'aw-org-pw-imenu-entries)
  (setq imenu-default-goto-function 'aw-org-pw-imenu-goto)

  (add-hook 'isearch-mode-hook 'aw-org-pw-isearch-start nil t)
  (add-hook 'isearch-mode-end-hook 'aw-org-pw-isearch-end nil t)

  (widen)
  (show-all)
  (aw-org-pw-update-sections-regexp)
  (aw-org-pw-frontpage))


;;;;
;;;; Keymaps
;;;;

(define-key aw-org-pw-mode-map "\C-c\C-o" 'aw-org-pw-follow-link-at-point)
(define-key aw-org-pw-mode-map "\C-c\C-b" 'aw-org-pw-back)
(define-key aw-org-pw-mode-map "\C-c\C-f" 'aw-org-pw-frontpage)
(define-key aw-org-pw-mode-map "*" 'aw-org-star)

(defvar aw-org-pw-link-map (make-sparse-keymap))
(define-key aw-org-pw-link-map [mouse-2] 'aw-org-pw-follow-link-at-mouse)
(when org-mouse-1-follows-link
  (define-key aw-org-pw-link-map [follow-link] 'mouse-face))

(define-key aw-org-pw-link-map [return] 'aw-org-pw-follow-link-at-point)

(provide 'aw-org-pw)

;;; aw-org-pw.el ends here

[-- Attachment #3: aw-org-pw-example.org --]
[-- Type: text/plain, Size: 829 bytes --]

# -*- mode: aw-org-pw -*-

This is an example document.

Here is a link to a different section about features.

A SandBox page can be useful for testing.

** QUICK HELP:
 C-c C-o: follows link (or creates new page, try on a transient mark!)
 C-c C-b: back in navigation history
 C-c C-f: Back to front page

* Features
As this mode derives from standard org-mode all org-mode features are
available, such as file:///etc/motd links and code blocks.

#+BEGIN_SRC c
int main(int argc, char **argv);
#+END_SRC

BTW you can press C-c C-b to go back to previously visited page.


* SandBox page
The "StartPage" page doesn't really exist, it is the part of the
document upto first header.

Remember that first level headings means different sections. So a
bullet list in a section should be second level, such as:
** foo
** bar
** baz


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Personal wiki
  2011-07-23 21:25 Personal wiki Anders Waldenborg
@ 2011-07-24 19:22 ` Bastien
  2011-07-24 19:57   ` Anders Waldenborg
  0 siblings, 1 reply; 5+ messages in thread
From: Bastien @ 2011-07-24 19:22 UTC (permalink / raw)
  To: Anders Waldenborg; +Cc: emacs-orgmode

Hi Ander,

Anders Waldenborg <anders@0x63.nu> writes:

> Maybe someone else is interested in my "personal wiki" I implemented
> on top of org-mode.

Thanks for sharing it!

> It basically adds three things to plain org-mode
> * Narrowing
>   Makes only current section[*] visible and thus avoids any distraction
>   by unrelated things.

Org has `C-x n s' to narrow to a subtree.

> * Automatic links of all text matching a section name.
>   Much like org-wikinodes, but without camel case.

In Org, when clicking on [[my section]] it goes to the next headline
called * My section.  I guess your implementation of such a feature
don't need surrounding [[...]].  

> * Navigation history
>   When visiting different sections in the same file history is
>   recorded allowing navigation "back" to previous section.

Could you describe the difference with `C-c &'?

> Find the el file attached, and also an example file.
>
> What do you think?

I think it can nurture discussion about the above features, so 
that's great!  Thanks again,

-- 
 Bastien

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Personal wiki
  2011-07-24 19:22 ` Bastien
@ 2011-07-24 19:57   ` Anders Waldenborg
  2011-07-26  0:51     ` Bastien
  0 siblings, 1 reply; 5+ messages in thread
From: Anders Waldenborg @ 2011-07-24 19:57 UTC (permalink / raw)
  To: Bastien; +Cc: emacs-orgmode

On Sun, Jul 24, 2011 at 09:22:57PM +0200, Bastien wrote:
> Org has `C-x n s' to narrow to a subtree.

Right. The difference is that aw-org-pw re-narrows when navigating to
a different section. Maybe that is a feature that would be useful
directly in org instead. Maybe pre/post-link-follow hooks could do that?

Also aw-org-pw autowidens on isearch. (the reason for that is that when
used as a personal wiki each section should be small, so searching is
seldom required within a section, and searching the full "wiki" is
more useful).

(aw-org-pw narrowing hides the actual header line too, partially to
prevent renaming sections as they have links to them, and partially
because it already puts the title in the header-line)

> > * Automatic links of all text matching a section name.
> >   Much like org-wikinodes, but without camel case.
> 
> In Org, when clicking on [[my section]] it goes to the next headline
> called * My section.  I guess your implementation of such a feature
> don't need surrounding [[...]]. 

Indeed, much like org-wikinodes.el.

But the more I think about it maybe I should just use [[...]] as it
would greatly simplify the implementation.

> 
> > * Navigation history
> >   When visiting different sections in the same file history is
> >   recorded allowing navigation "back" to previous section.
> 
> Could you describe the difference with `C-c &'?

Oh, I wasn't aware of the existance of the org mark ring. Interesting.
It doesn't work outside narrowed region. Also the breadcrumbs in
aw-org-pw keeps track of the section name, which and shows "back:
Previouslyvisitedsection" in the header-line.

But I guess I should build on top of the org mark ring instead of
reinventing it completely.

Thanks for your input!

 anders

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Personal wiki
  2011-07-24 19:57   ` Anders Waldenborg
@ 2011-07-26  0:51     ` Bastien
  2011-07-29 20:58       ` Anders Waldenborg
  0 siblings, 1 reply; 5+ messages in thread
From: Bastien @ 2011-07-26  0:51 UTC (permalink / raw)
  To: Anders Waldenborg; +Cc: emacs-orgmode

Hi Anders,

Anders Waldenborg <anders@0x63.nu> writes:

> On Sun, Jul 24, 2011 at 09:22:57PM +0200, Bastien wrote:
>> Org has `C-x n s' to narrow to a subtree.
>
> Right. The difference is that aw-org-pw re-narrows when navigating to
> a different section. Maybe that is a feature that would be useful
> directly in org instead. Maybe pre/post-link-follow hooks could do
> that?

Mmhh.. do you really use this often?  I mean: it's quite useful to work
from a wide buffer when navigating -- narrowing is just for quickly
focusing on something.  (But maybe I should rather test your file and
see how useful it can be.)

> Also aw-org-pw autowidens on isearch. (the reason for that is that when
> used as a personal wiki each section should be small, so searching is
> seldom required within a section, and searching the full "wiki" is
> more useful).

Okay - since narrowing in Org is not that frequent and Org files can 
be very large, I guess it's better to *not* auto-widen on isearch.

> But the more I think about it maybe I should just use [[...]] as it
> would greatly simplify the implementation.

:)

> Thanks for your input!

You're welcome,

-- 
 Bastien

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Personal wiki
  2011-07-26  0:51     ` Bastien
@ 2011-07-29 20:58       ` Anders Waldenborg
  0 siblings, 0 replies; 5+ messages in thread
From: Anders Waldenborg @ 2011-07-29 20:58 UTC (permalink / raw)
  To: Bastien; +Cc: emacs-orgmode

On Tue, Jul 26, 2011 at 02:51:08AM +0200, Bastien wrote:
> > Right. The difference is that aw-org-pw re-narrows when navigating to
> > a different section. Maybe that is a feature that would be useful
> > directly in org instead. Maybe pre/post-link-follow hooks could do
> > that?

When investigating how to implement this cleanly I found some code in
org-open-at-point that widens if link isn't found:

    (condition-case nil (eval cmd)
          (error (progn (widen) (eval cmd))))))


However I think there is a bug in there.

(eval cmd) will cause a "No match - create this as a new heading? (y
or n)" question if the link couldn't be found. So I think it should be:

    (condition-case nil (let ((org-link-search-inhibit-query t))
                           (eval cmd))
          (error (progn (widen) (eval cmd))))))
	    
Otherwise one would need to answer "n" to the question to widen and
find the link.

Should I submit a proper bugreport (and a patch?).

> Mmhh.. do you really use this often?  I mean: it's quite useful to work
> from a wide buffer when navigating -- narrowing is just for quickly
> focusing on something.  (But maybe I should rather test your file and
> see how useful it can be.)

In my setup I have very different topics in same file, and the reason
I want to narrow is to avoid getting distracted. I want to emulate a
wiki with different pages. And a such I don't expect things to put in
a proper hierarchy, meaning that there will just be a bunch of
disorganized top level headers.

Without narrowing I would be looking at something like:

"""
* Unrelated heading...
* Another unrelated heading...
* Something I find interesting but don't want to think about now...
* The "page" I'm viewing

here be the contents I want to focus on now

* Hey I'm a heading that wants to distract you...
"""


> > Also aw-org-pw autowidens on isearch. (the reason for that is that when
> > used as a personal wiki each section should be small, so searching is
> > seldom required within a section, and searching the full "wiki" is
> > more useful).
> 
> Okay - since narrowing in Org is not that frequent and Org files can 
> be very large, I guess it's better to *not* auto-widen on isearch.

I created a separate (and not org specific) minor-mode that autowidens
on isearch.

 anders

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-07-29 20:59 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-23 21:25 Personal wiki Anders Waldenborg
2011-07-24 19:22 ` Bastien
2011-07-24 19:57   ` Anders Waldenborg
2011-07-26  0:51     ` Bastien
2011-07-29 20:58       ` Anders Waldenborg

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).