Skip to content

Commit

Permalink
update parsing for simple mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua Tazman Reinier committed Aug 9, 2023
1 parent 53dffcc commit 52432bd
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 39 deletions.
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@
- Add support for [Full Calendar full notes](https://github.com/joshuatazrein/obsidian-time-ruler/issues/10#issuecomment-1655804209)

## Planned
- Option to [hide/show headings](https://github.com/joshuatazrein/obsidian-time-ruler/issues/11#issuecomment-1655862428) to reduce visual clutter
- Add support for [task repeats](https://github.com/joshuatazrein/obsidian-time-ruler/issues/5#issuecomment-1646958839)
- Option to [add tasks at start or end of headings](https://github.com/joshuatazrein/obsidian-time-ruler/issues/12)
- Right-click option to [schedule tasks for now](https://github.com/joshuatazrein/obsidian-time-ruler/issues/16#event-9959008621)
- More specific Dataview custom filter [at task level](https://github.com/joshuatazrein/obsidian-time-ruler/issues/18)
- Options to drag [deadlines and reminder times](https://github.com/joshuatazrein/obsidian-time-ruler/issues/20) in addition to scheduled time
- A [simple mode](https://github.com/joshuatazrein/obsidian-time-ruler/issues/21) with `HH:mm-HH:mm` formatting for scheduled times.

## Considering
- Providing [names for time blocks](https://github.com/joshuatazrein/obsidian-time-ruler/issues/11#issuecomment-1655862428) (perhaps by moving tasks with durations to the "name" field of a time block)
- Option to [hide/show headings](https://github.com/joshuatazrein/obsidian-time-ruler/issues/11#issuecomment-1655862428) to reduce visual clutter

# Changelog

## 1.0.6 (Upcoming)
## 1.1.0 (Upcoming)
- **Added:** Support [emoji and custom status](https://github.com/joshuatazrein/obsidian-time-ruler/issues/26) displaying in tasks
- **Added:** Filter by [custom status](https://github.com/joshuatazrein/obsidian-time-ruler/issues/25)
- **Added:** A [simple mode](https://github.com/joshuatazrein/obsidian-time-ruler/issues/21) with `HH:mm-HH:mm` formatting for scheduled times.
- **Added:** larger drop target for [dates](https://github.com/joshuatazrein/obsidian-time-ruler/issues/24)
- **Improved:** Moved search, refresh, and view buttons to a collapsible menu

## 1.0.5 (8/6/2023)
Expand Down
10 changes: 6 additions & 4 deletions src/components/Droppable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function Droppable({
children,
id,
data,
ref
ref,
}: {
children: JSX.Element
id: string
Expand All @@ -14,16 +14,18 @@ export default function Droppable({
}) {
const { isOver, setNodeRef } = useDroppable({
id,
data
data,
})

return cloneElement(children, {
ref: ref
? node => {
? (node) => {
ref(node)
setNodeRef(node)
}
: setNodeRef,
className: `${children.props.className} ${isOver ? '!bg-selection' : ''}`
className: `${children.props.className} rounded-lg ${
isOver ? '!bg-selection' : ''
}`,
})
}
39 changes: 38 additions & 1 deletion src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ export default function Search() {
return searchTasks
}, [tasksByHeading])

const [status, setStatus] = useState<string | null>(null)

const allStatuses = useMemo(() => {
return [
...new Set(
Object.values(tasksByHeading)
.flat()
.map((task) => task.status)
),
].sort()
}, [tasksByHeading])

const searching = !!searchStatus

return (
Expand Down Expand Up @@ -177,6 +189,30 @@ export default function Search() {
placeholder='path: heading: title: tag: priority:'
ref={inputFrame}
></input>
{allStatuses.length > 1 && (
<div className='group relative h-fit flex-none'>
<Button className='clickable-icon'>
{status === ' ' ? '[ ]' : status ?? 'status'}
</Button>
<div className='absolute right-0 top-full z-30 hidden rounded-lg border-solid border-faint bg-primary group-hover:block'>
<Button
className='clickable-icon w-full'
onClick={() => setStatus(null)}
>
clear
</Button>
{allStatuses.map((status) => (
<Button
key={status}
className='clickable-icon h-line w-full'
onClick={() => setStatus(status)}
>
{status === ' ' ? '[ ]' : status}
</Button>
))}
</div>
</div>
)}
</div>
{typeof searchStatus === 'string' && (
<div className='space-1 -mt-1 flex flex-wrap'>
Expand Down Expand Up @@ -216,6 +252,7 @@ export default function Search() {
const filteredTasks = isShowingTasks
? tasksByHeading[heading]?.filter(
(task) =>
!(status && task.status !== status) &&
searchExp.test(searchTasks[task.id]) &&
testViewMode(task)
) ?? []
Expand All @@ -226,7 +263,7 @@ export default function Search() {
)
return (
<Fragment key={heading}>
{(searchStatus === 'all' ||
{((searchStatus === 'all' && !status) ||
!isShowingTasks ||
filteredTasks.length > 0) && (
<div key={heading} className='my-4'>
Expand Down
14 changes: 10 additions & 4 deletions src/components/Task.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ export default function Task({
).replace(/\.md/, '')
}, [type, task.path])

if (task.title.includes('pioneer')) console.log(task.title, task)

if (!task) return <></>

return (
Expand Down Expand Up @@ -131,10 +133,12 @@ export default function Task({
<Button
onPointerDown={() => false}
onClick={() => completeTask()}
className={`selectable flex-none rounded-checkbox border border-solid border-faint bg-transparent p-0 shadow-none hover:border-normal ${
className={`selectable flex flex-none items-center justify-center rounded-checkbox border border-solid border-faint bg-transparent p-0 pb-[1px] text-xs shadow-none hover:border-normal ${
isLink ? 'h-2 w-2' : 'h-4 w-4'
}`}
/>
>
{task.status}
</Button>
</div>
<div
className={`w-fit min-w-[24px] cursor-pointer break-words leading-line hover:underline ${
Expand All @@ -144,10 +148,12 @@ export default function Task({
? 'text-accent'
: type === 'deadline'
? ''
: task.priority === TaskPriorities.LOW || isLink
: task.priority === TaskPriorities.LOW ||
isLink ||
task.status === 'x'
? 'text-faint'
: ''
}`}
} ${task.status === 'x' ? 'line-through' : ''}`}
onPointerDown={() => false}
onClick={() => openTask(task)}
>
Expand Down
29 changes: 12 additions & 17 deletions src/components/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,6 @@ export default function Timeline({
)
}, shallow)

// const filterAllDayChildren = (tasks: TaskProps[]) => {
// const thisTaskIds = new Set(tasks.map((task) => task.id))
// return tasks.filter(
// (task) =>
// !(
// task.parent &&
// thisTaskIds.has(task.parent) &&
// task.scheduled &&
// isDateISO(task.scheduled)
// )
// )
// }

const isToday =
startISO.slice(0, 10) === (DateTime.now().toISODate() as string)

Expand All @@ -59,12 +46,14 @@ export default function Timeline({
const allDayTasks: TaskProps[] = []
_.forEach(state.tasks, (task) => {
const scheduledForToday =
!task.completion &&
task.scheduled &&
task.scheduled < endISO &&
(isToday || task.scheduled >= startISO)
if (
!scheduledForToday &&
task.due &&
!task.completion &&
(task.due >= startISO || (isToday && task.due < endISO))
) {
dueTasks.push(task)
Expand Down Expand Up @@ -157,8 +146,8 @@ export default function Timeline({
</div>
</Droppable>
<div
className={`h-0 grow space-y-2 ${
calendarMode ? 'overflow-y-auto' : 'flex flex-col'
className={`flex h-0 grow flex-col space-y-2 ${
calendarMode ? 'overflow-y-auto' : ''
}`}
data-auto-scroll={calendarMode ? 'y' : undefined}
>
Expand Down Expand Up @@ -201,12 +190,18 @@ export default function Timeline({
)}

<div
className={`w-full overflow-x-hidden rounded-lg ${
calendarMode ? '' : 'h-full overflow-y-auto'
className={`flex h-0 w-full grow flex-col overflow-x-hidden rounded-lg ${
calendarMode ? '' : 'overflow-y-auto'
}`}
data-auto-scroll={calendarMode ? undefined : 'y'}
>
{timeSpan}
<Droppable
data={{ scheduled: startISO }}
id={startISO + '::timeline::end'}
>
<div className='h-0 grow'></div>
</Droppable>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const DEFAULT_SETTINGS: TimeRulerSettings = {
fileOrder: [],
customStatus: {
include: false,
statuses: '-',
statuses: 'x-',
},
}

Expand Down
2 changes: 0 additions & 2 deletions src/services/obsidianApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ export default class ObsidianAPI extends Component {
.filter((task) => {
return (
taskTest.test(task.status) === this.settings.customStatus.include &&
!task.completion &&
!task.completed &&
!(this.excludePaths && this.excludePaths.test(task.path)) &&
!(
task.start &&
Expand Down
82 changes: 76 additions & 6 deletions src/services/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
keyToTasksEmoji,
priorityKeyToNumber,
priorityNumberToKey,
priorityNumberToSimplePriority,
simplePriorityToNumber,
} from '../types/enums'
import _ from 'lodash'
import { isDateISO } from './util'
Expand All @@ -29,13 +31,20 @@ export function textToTask(item: any): TaskProps {
const REMINDER_MATCH = new RegExp(
` ?${keyToTasksEmoji.reminder} ?(${ISO_MATCH}( \\d{2}:\\d{2})?)|\\(@(\\d{4}-\\d{2}-\\d{2}( \\d{2}:\\d{2})?)\\)|@\\{(\\d{4}-\\d{2}-\\d{2}( \\d{2}:\\d{2})?)\\}`
)
const SIMPLE_SCHEDULED = /^\d+:\d+( ?- ?\d+:\d+)?/
const SIMPLE_PRIORITY = / (?|!|!!|!!!)$/
const SIMPLE_DUE = / > (\d{4}-d{2}-d{2})/

const originalTitle: string = (item.text.match(/(.*?)(\n|$)/)?.[1] ?? '')
const titleLine: string = item.text.match(/(.*?)(\n|$)/)?.[1] ?? ''
const originalTitle: string = titleLine
.replace(INLINE_FIELD_SEARCH, '')
.replace(TASKS_REPEAT_SEARCH, '')
.replace(TASKS_EMOJI_SEARCH, '')
.replace(TAG_SEARCH, '')
.replace(REMINDER_MATCH, '')
.replace(SIMPLE_SCHEDULED, '')
.replace(SIMPLE_DUE, '')
.replace(SIMPLE_PRIORITY, '')
let title: string = originalTitle
.replace(MD_LINK_SEARCH, '$1')
.replace(LINK_SEARCH, '$1')
Expand Down Expand Up @@ -89,6 +98,7 @@ export function textToTask(item: any): TaskProps {
const parseScheduled = () => {
let scheduled = item.scheduled as DateTime | undefined
let isDate: boolean = false

if (!scheduled) {
let date = item.date as string | undefined
const testDailyNoteTask = !date && item.parent === undefined
Expand Down Expand Up @@ -184,17 +194,48 @@ export function textToTask(item: any): TaskProps {
}

const parseRepeat = () => {
return item['repeat'] ?? item.text.match(TASKS_REPEAT_SEARCH)?.[1]
return item['repeat'] ?? titleLine.match(TASKS_REPEAT_SEARCH)?.[1]
}

const scheduled = parseScheduled()
const due = parseDateKey('due')
const parseSimple = () => {
let simpleScheduled: string | undefined,
simpleLength: TaskProps['length'] | undefined,
simpleDue: TaskProps['due'] | undefined,
simplePriority: TaskProps['priority'] | undefined
const scheduled = titleLine.match(SIMPLE_SCHEDULED)?.[0]
if (scheduled) {
const [startTime, endTime] = scheduled.split(/ ?- ?/)
const date = item.section.path
.replace(/\.md$/, '')
.match(new RegExp(`${ISO_MATCH}$`))?.[0]
if (date && startTime) {
simpleScheduled = date + 'T' + startTime
}
if (simpleScheduled && endTime) {
const duration = DateTime.fromISO(date + 'T' + endTime)
.diff(DateTime.fromISO(simpleScheduled))
.shiftTo('hour', 'minute')
simpleLength = { hour: duration.hours, minute: duration.minutes }
}
}
const priorityMatch = titleLine.match(SIMPLE_PRIORITY)?.[1]
simplePriority = priorityMatch
? simplePriorityToNumber[priorityMatch]
: undefined
simpleDue = titleLine.match(SIMPLE_DUE)?.[1]
return { simpleScheduled, simpleLength, simplePriority, simpleDue }
}

const { simpleScheduled, simpleLength, simpleDue, simplePriority } =
parseSimple()
const scheduled = simpleScheduled ?? parseScheduled()
const due = simpleDue ?? parseDateKey('due')
const completion = parseDateKey('completion')
const start = parseDateKey('start')
const created = parseDateKey('created')
const repeat = parseRepeat()
const priority = parsePriority()
const length = parseLength(scheduled)
const priority = simplePriority ?? parsePriority()
const length = simpleLength ?? parseLength(scheduled)
const reminder = parseReminder()

return {
Expand Down Expand Up @@ -231,6 +272,7 @@ const detectFieldFormat = (
defaultFormat: FieldFormat['main']
): FieldFormat => {
const parseMain = (): FieldFormat['main'] => {
if (/^\d+:\d+( ?- ?\d+:\d+)? /.test(text)) return 'simple'
for (let emoji of Object.keys(TasksEmojiToKey)) {
if (text.contains(emoji)) return 'tasks'
}
Expand Down Expand Up @@ -283,6 +325,34 @@ export function taskToText(
}

switch (main) {
case 'simple':
if (task.scheduled && !isDateISO(task.scheduled)) {
let scheduledTime = task.scheduled.slice(11, 16)
if (task.length) {
const end = DateTime.fromISO(task.scheduled).plus(task.length)
scheduledTime += ` - ${end.toFormat('HH:mm')}`
}
draft =
draft.slice(0, 6) +
scheduledTime +
' ' +
draft.slice(6).replace(/^\s+/, '')
}
if (task.due) draft += ` > ${task.due}`
if (task.priority && task.priority !== TaskPriorities.DEFAULT) {
draft += ` ${priorityNumberToSimplePriority[task.priority]}`
}
if (task.repeat) draft += ` [repeat:: ${task.repeat}]`
if (task.start) {
draft += ` [start:: ${task.start}]`
}
if (task.created) {
draft += ` [created:: ${task.created}]`
}
if (task.completion) {
draft += ` [completion:: ${task.completion}]`
}
break
case 'dataview':
if (task.scheduled) draft += ` [scheduled:: ${task.scheduled}]`
draft += formatReminder()
Expand Down
8 changes: 8 additions & 0 deletions src/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export const priorityKeyToNumber = {
default: TaskPriorities.DEFAULT,
}

export const simplePriorityToNumber = {
'?': TaskPriorities.LOWEST,
'!': TaskPriorities.LOW,
'!!': TaskPriorities.HIGH,
'!!!': TaskPriorities.HIGHEST,
}

export const priorityNumberToSimplePriority = _.invert(simplePriorityToNumber)
export const priorityNumberToKey = _.invert(priorityKeyToNumber)

export const keyToTasksEmoji = {
Expand Down
Loading

0 comments on commit 52432bd

Please sign in to comment.